剑指offer 2.2 - 赋值运算函数

来源:互联网 发布:网络说唱歌曲 编辑:程序博客网 时间:2024/06/05 19:59
3、程序问题分析
A. 编译错误; B 编译成功,运行时程序崩溃; C 编译运行正常,输出10.
Class A{private:    int value;public:    A(int n) {value = n;}    A(A other) {value = other.value;}    void print(){std::cout << value << std:: endl;}}int main(){    A a = 10;    A b = a;    b.print();    return 0;}
在上述代码中,复制构造函数A(A other)传入的参数是A的一个实例。
由于是传值参数, 我们把形参复制到实参会调用复制构造函数。
因此,如果允许复制构造函数传值,就会在复制构造函数内调用复制构造函数,就会形成永无休止的递归调用
从而导致栈溢出。因此C++的标准不允许复制构造函数传值参数。在Visual Studio和GCC中,豆浆编译出错。
可以改为A(const A& other),也就是把传值参数改成常量引用。
================================================================================
面试题1 : 赋值运算符函数
如下为类型CMyString的声明,请为该类型添加赋值运算符函数。
class CMyString{ public:        CMyString(char * pData = NULL);          CMyString(const CMyString& str);       ~ CMyString(void );private:        char * m_pData;}
面试者考察的关键点:
1、是否把返回值的类型声明为该类型的引用,并在函数结束前返回实例自身的引用(即*this)。
        只有返回一个引用,才可以允许连续赋值。
        否则,如果函数返回值是void,应该该复制运算将不会做连续赋值。
        假设有3个CMyString的对象:str1、str2和str3,在程序中str1=str2=str3将不能通过编译。
2、是否把传入的参数声明为常量引用。
        如果传入的是实例,会造成复制构造函数。
        同时要为传入的引用参数加上const关键字。
3、是否释放实例自身已有的内存。
        否则会出现内存泄露。
4、是否判断传入的参数和当前的实例(*this)是不是同一个实例。
        如果是一个,则不能进行赋值操作,直接返回。
        如果不判断,那么在释放实例自身的内存时,就会导致严重的问题:当*this和传入的参数时同一个实例时,那么
        一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此再也找不到需要赋值的内容了。

CMyString& CMyString :: operator = (const CMyString &str) {        // 如果传入的参数与当前的实例是同一个实例,则直接返回自身        if(this == &str) return *this;        // 释放实例自身已有内存        delete []m_pData;        m_pData = NULL;        // 在删除自身以后重新new一个长度为len+1的字符数组,类似拷贝构造函数        m_pData = new char[strlen(str.m_pData) + 1];        strcpy(m_pData, str.m_pData);        return *this;}
上述中,在分配内存之前先用delete释放了实例m_pData的内存。如果此时内存不足导致new char  抛出异常, m_pData将是一个空指针,这非常容易导致程序崩溃。也就是说一旦在赋值运算符函数内部抛出一个异常, CMyString的实例不再保持有效的状态,违背了异常安全性的原则,有两个解决方法:
1、先用new分配出新内容再用delete释放已有的内容,只有在分配内存成功之后再释放原来的内容,也就是当分配内存
失败时我们能够保证CMyString的实例不会被修改。
2、更好的方法:先创建一个临时实例,再交换临时实例和原来的实例,下面是代码:
CMyString& CMyString :: operator = (const CMyString &str) {            if(this != &str)        {                CMyString strTemp(str); // 使用构造函数创建一个临时对象, 出了strTemp的作用域,                                                            // 该实例会被自动调用析构函数,把其所指向的内存释放掉。                            // 交换临时对象与当前对象的m_pData值                 char* pTemp = strTemp.m_pData;                strTemp.m_pData = m_pData;                m_pData = pTemp;        }        return *this;}

0 0
原创粉丝点击