这是一道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模块,这样就能处理异常,同时不会是程序终止。改进的实现的唯一目的就是为了保证在抛出异常(可能因为内存不足)时,原来的实例没有被修改过。也就是说,要抛异常你就早点抛,在我修改原来实例之前就抛,要么你就不要抛。

C++重载赋值运算符的更多相关文章

  1. 重载赋值运算符 && 对象

    class CMessage { private: char * m_pMessage; public: void showIt()const { cout << m_pMessage & ...

  2. mfc 重载赋值运算符

    重载赋值运算符= 一.重载运算符格式 返回类型 operator 运算符 (参数); 如: bool operator=(char*s); int operator>(char*s); bool ...

  3. 《剑指offer》第一题(重载赋值运算符)

    //重载赋值运算符 #include <iostream> #include <cstring> using namespace std; class CMystring { ...

  4. 《挑战30天C++入门极限》C++运算符重载赋值运算符

        C++运算符重载赋值运算符 自定义类的赋值运算符重载函数的作用与内置赋值运算符的作用类似,但是要要注意的是,它与拷贝构造函数与析构函数一样,要注意深拷贝浅拷贝的问题,在没有深拷贝浅拷贝的情况下 ...

  5. C++本质:类的赋值运算符=的重载,以及深拷贝和浅拷贝

    关键词:构造函数,浅拷贝,深拷贝,堆栈(stack),堆heap,赋值运算符摘要:    在面向对象程序设计中,对象间的相互拷贝和赋值是经常进行的操作.    如果对象在申明的同时马上进行的初始化操作 ...

  6. C++学习31 重载=(赋值运算符)

    和普通变量一样,对象之间也可以相互赋值.赋值运算符“=”可以用来将一个对象拷贝给另一个已经存在的对象.对象之间的赋值是将成员变量依次拷贝,而不是将整个对象的内存按位拷贝. 对象之间的赋值: #incl ...

  7. C++的转换构造函数、拷贝构造函数、赋值运算符重载

    1 转换构造函数     C++的转换构造函数是只有一个参数的构造函数.当程序试图将一个其他类型的对象或基本类型值赋给该类的一个待初始化对象时(如Person p="Dean";) ...

  8. C++ 赋值运算符'='的重载(浅拷贝、深拷贝)

    01 赋值运算符重载的需求 有时候希望赋值运算符两边的类型可以不匹配,比如:把一个 int 类型变量赋值给一个Complex(复数)对象,或把一个 char* 类型的字符串赋值给一个字符串对象,此时就 ...

  9. C++ 中赋值运算符重载以及深拷贝浅拷贝解析

    转载自:http://blog.csdn.net/business122/article/details/21242857 关键词:构造函数,浅拷贝,深拷贝,堆栈(stack),堆heap,赋值运算符 ...

随机推荐

  1. XCode破解真机调试

    XCode破解真机调试  3.0 一.这样做以后能怎样 以device模式编译出app 可以再越狱后的设备上运行 二.要会点什么 命令行,也就是terminal.终端.控制台... vim 三.开始吧 ...

  2. oracle job 定时执行 存储过程

    oracle job 定时执行 存储过程   一:简单测试job的创建过程案例: 1,先创建一张JOB_TEST表,字段为a 日期格式 SQL> create table JOB_TEST(a ...

  3. Trunk Club:颠覆男士时装零售的创业公司_第1页_福布斯中文网

    Trunk Club:颠覆男士时装零售的创业公司_第1页_福布斯中文网 Trunk Club:颠覆男士时装零售的创业公司

  4. 剑指offer 22 栈的压入、弹出序列

    class Solution { public: bool IsPopOrder(vector<int> pushV,vector<int> popV) { bool resu ...

  5. 日期选择器——java

    转载:http://zgdeng.iteye.com/blog/1405650 代码如下: import java.awt.BasicStroke; import java.awt.BorderLay ...

  6. Deep Learning(深度学习)学习笔记整理系列之(二)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

  7. iOS 导航条的影响

    如果是push出来的控制器,self.view的(0,0)点从状态栏下面开始: 如果有present出来的控制器,self.view的(0,0)点包含状态栏:

  8. 输出多行字符的一个简单JAVA小程序

    public class JAVA { public static void main(String[] args) { System.out.println("-------------- ...

  9. 无法连接远程mysql问题

    按照别人的博客操作之后,重启了服务: 前几天,windows上的mysql无法远程连接,在网上搜了一下,都是一个说法,神马改表.加权限.刷新等等,尝试之后都没有用,后来同事告诉了我一个方法,就解决了那 ...

  10. C++学习之文件的输入输出

    C++学习之文件的输入输出        一.文件的打开与关闭        1.输出数据到文件        文件的操作需要包含fstream头文件,文件的操作对象为ifstream,ofstrea ...