剑指offer 2.2 - 赋值运算函数

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值