面试中的赋值运算符函数

来源:互联网 发布:网络教育和自考 编辑:程序博客网 时间:2024/05/23 00:17
   当一个面试官要求应聘者定义一个赋值运算符函数时,他会在检查应聘者写出的代码中关注如下几点:

       (1)是否把返回值的类型声明为该类的引用,并在函数结束的时侯返回示例自身的引用(即*this).只有返回一个引用,才可以允许连续赋值。

      (2)是否把参数的类型声明为常量引用。如果传入的参数不是引用而是实例,那么从形参到实参会调用一次构造函数。把参数声明为引用时可以避免这样无谓的消耗,能提高代码的效率。

       (3)是否释放实例自身已有的内存。如果忘记释放,会导致内存的泄漏。

        (4)是否判断传入的参数和当前的实例是不是同一个实例。如果是同一个,则不用进行赋值,直接返回。如果不进行判断时,会导致比较严重的问题就是,赋值的内容被删除了。

     下面给出一个MyString类的实现:

#include<iostream>#include<string.h>#include<stdio.h>#include<stdlib.h>using namespace std;class MyString{public:    MyString(char *pdata = NULL);    MyString(const MyString& str);    MyString& operator=(const MyString& str);    ~MyString(void);    void Print();private:    char *m_pdata;};//构造函数MyString::MyString(char *pdata){    if(pdata == NULL){        m_pdata = new char[1];        m_pdata[0] = '\0';    }else{        int len = strlen(pdata);        m_pdata = new char[len + 1];        strcpy(m_pdata,pdata);    }}//拷贝构造函数MyString::MyString(const MyString& str){    int length = strlen(str.m_pdata);    m_pdata = new char[length + 1];    strcpy(m_pdata,str.m_pdata);}//析构函数MyString::~MyString(){    delete []m_pdata;}//赋值运算MyString& MyString::operator = (const MyString& str){    if(this == &str){        return *this;    }    delete []m_pdata;    m_pdata = NULL;        m_pdata = new char[strlen(str.m_pdata) + 1];    strcpy(m_pdata,str.m_pdata);    return *this;}void MyString::Print(){    printf("%s", m_pdata);}void Test1(){    printf("Test1 begins:\n");    char text[] = "Hello world";    MyString str1(text);    MyString str2;    str2 = str1;    printf("The expected result is: %s.\n", text);    printf("The actual result is: ");    str2.Print();    printf(".\n");}void Test2(){    printf("Test2 begins:\n");    char text[] = "Hello world";    MyString str1(text);    str1 = str1;    printf("The expected result is: %s.\n", text);    printf("The actual result is: ");    str1.Print();    printf(".\n");}void Test3(){    printf("Test3 begins:\n");    char text[] = "Hello world";    MyString str1(text);    MyString str2, str3;    str3 = str2 = str1;    printf("The expected result is: %s.\n", text);    printf("The actual result is: ");    str2.Print();    printf(".\n");    printf("The expected result is: %s.\n", text);    printf("The actual result is: ");    str3.Print();    printf(".\n");}int main(int argc,char** argv){    Test1();    Test2();    Test3();    return 0;}

我们先来看测试程序的执行结果:

  根据测试的程序来看,赋值运算符函数基本没有什么问题了。

     上述对于赋值运算符函数的实现只适用于初级的程序员,要想和别人不一样,我们就需要考虑下面的问题:

  在前面的函数中,我们在分配内存之前先用delete释放了实例m_pdata的内存。如果此时内存不足导致new char抛出异常,m_pdata将是一个空指针,这样非常容易导致程序崩溃。也就是说一旦在赋值运算符内部抛出了一个异常,实例将不再保持原有的有效状态,这就违背了异常安全性的原则。

    想要解决这个问题我们有两种办法:(1)一个简单的办法就是我们先用new 分配新内容再用delete释放已有的内容。这样只在分配i内容成功之后再释放原来的内容,当分配失败时,我们能确保原来的实例还存在。

 (2)第二个更好的办法就是,先创建一个临时实例,再交换临时实例和原来的实例。

 下面我们给出第二种办法的代码:

MyString& MyString::operator = (const MyString& str){    if(this != &str){        MyString strtemp(str);                char *temp = strtemp.m_pdata;        strtemp.m_pdata = m_pdata;        m_pdata = temp;    }    return *this;}

局部的对象strtemp在函数执行完就要调用析构函数,利用析构函数释放了原来实例的内容。。。


0 0
原创粉丝点击