zoukankan      html  css  js  c++  java
  • C++重载赋值运算符

    这是一道C++的面试题,下面在这篇博客中分析一下这个问题。先上题目:

    //题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数。
    class CMyString
    {
    public:
    CMyString(char *pData=NULL);//构造函数
    CMyString(const CMyString& str);//拷贝构造函数
    ~CMyString();//析构函数
    private:
    char* m_pData;//数据域,字符指针
    };
    

      

    拿到这个题目,如果你看过effective C++这本书,代码应该很容易就能够完成,在写的时候要注意下面的一个要点:

    1>赋值运算符要返回该实例自身的引用(*this),因为只有返回一个引用才可以允许连续赋值操作。

    2>把传入的参数的类型声明为常量引用,这样能够避免传入实例时的值拷贝,避免复制构造函数的调用。同时赋值运算符不会改变传入的实例的状态,因此要在传入的参数前加上const关键字。

    3>要在分配新的内存之前释放自身已有的空间,否则程序会出现内存泄露。赋值运算符执行的是赋值操作,在赋值运算符之前肯定调用过对象的构造函数或者拷贝构造函数,因为一个对象在实例化的时候肯定会有一个初始化的过程。这里必须释放原来的堆内存空间,因为m_pData可能变得更长了,原来的空间可能已经不够用了

    4>判断传入的参数和当前的实例(*this)是不是同一个实例,如果是同一个实例的话就直接返回,防止str1=str1这样的没有意义的操作。如果不判断这个 ,结果很严重,如果释放了实例的堆内存空间,也就等于释放了参数的堆空间(虽然参数是const的,但是这次的释放是通过this对象释放的,不是通过参数对象释放的),就没有了这个对象的完整备份,那么这个对象就永远的从这个世界上消失了。

    CMyString& CMyString::operator=(const CMyString& str)
    {
    	if(this == &str)
    	{
    		return *this;
    	}
    
    	delete[] m_pData;
    	m_pData = NULL;
    
    	m_pData = (char*)malloc(strlen(str.m_pData) + 1);
    	strcpy(m_pData, str.m_pData);
    
    	return *this;
    }
    

      上面的代码虽然能够解决问题,但是存在安全隐患,在分配内存之前先用delete释放了实例m_pData的内存,如果此时内存不足,导致new char申请内存失败,m_pData将是一个空指针,这很容易发生异常,因为我们认为它不是一个空指针。

    这里要处理的是防止new堆内存的时候,由于内存不足等原因导致异常,而是当前对象的完整性遭到破坏。也就是说你要异常要在操作当前对象之前,不能在操作当前对象的过程中异常,使得这个当前对象不完整。

    解决的办法有两种:

    1>先用new分配新内容,然后再用delete释放已有内容,也就是分配成功之后再分配新的内容,这样就能保证在分配失败之后,原来的CMyString的实例不会被修改。

    2>先在栈中创建一个临时实例,然后在交换临时实例和原有实例

    下面给出2>的代码实现:

    CMyString& CMyString::operator=(const CMyString& str)
    {
    	if(this == &str)
    	{
    		return *this;
    	}
    
    
    	CMyString tempStr(str);
    
    	//交换临时实例和原有实例的指针指向
    	char *pData = tempStr.m_pData;
    	tempStr.m_pData = m_pData;
    	m_pData = pData;
    
    	return *this;
    }
    

      注意代码的技巧的地方在于,这个tempStr是在栈中开辟的临时的实例,在这个函数运行结束以后,这个函数栈要销毁,所以tempStr的析构函数会自动的调用,当析构函数调用的时候肯定会自动的释放原来的那块堆内存的。

    在新的代码中,在CMyString的构造函数里用new分配内存。如果由于内存不足抛出如bad_alloc等异常,我们还没有修改原来的实例的状态,因此实例的状态还是有效的。

    其实上面的代码应该有try-catch模块,这样就能处理异常,同时不会是程序终止。改进的实现的唯一目的就是为了保证在抛出异常(可能因为内存不足)时,原来的实例没有被修改过。也就是说,要抛异常你就早点抛,在我修改原来实例之前就抛,要么你就不要抛。

  • 相关阅读:
    VS项目配置halcon、opencv等(附加包含目录、包含目录等)
    Qt 异常总结(ubuntu环境)
    linux简单分区
    U盘启动盘制作工具
    Ubuntu常用命令
    Ubuntu安装后的基本设置(系统主题,终端字体,更换源,浏览器等)
    makefile
    Ubuntu终端执行单个cpp
    Windbg内核调试之四: Dump文件分析
    浅议Windows 2000/XP Pagefile组织管理
  • 原文地址:https://www.cnblogs.com/stemon/p/4625407.html
Copyright © 2011-2022 走看看