面试中的赋值运算符函数

   当一个面试官要求应聘者定义一个赋值运算符函数时,他会在检查应聘者写出的代码中关注如下几点:

       (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在函数执行完就要调用析构函数,利用析构函数释放了原来实例的内容。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值