这是一道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. date tod = boost::gregorian::day_clock::local_day(); //当前日期

    boost 时间和日期 - 苦逼码农 - 博客频道 - CSDN.NET date tod = boost::gregorian::day_clock::local_day(); //当前日期

  2. 织梦DEDECMS 首页列表页内容也时间日期调用标签

    DEDECMS利用strftime()函数格式化时间的所有参数详解,包括年份日期进制.小时格式等,大家收藏吧,呵. 日期时间格式 (利用strftime()函数格式化时间)0 dedecms首页时间标 ...

  3. hdu 4598 Difference(奇圈判定+差分约束)

    这是通化邀请赛的题,当时比赛的时候还完全没想法呢,看来这几个月的训练还是有效果的... 题意要求(1) |ai| < T for all i   (2) (vi, vj) in E <=& ...

  4. JS 之 offsetWidth\offsetleft

  5. C++_enum

    C++的enum可以限制成员的类型 //error C2440: “=”: 无法从“int”转换为“color” #include <iostream> using namespace s ...

  6. IE6兼容性问题及IE6常见bug详细汇总---转载

    1.IE6怪异解析之padding与border算入宽高 原因:未加文档声明造成非盒模型解析 解决方法:加入文档声明<!doctype html> 2.IE6在块元素.左右浮动.设定mar ...

  7. 【Deep Learning】genCNN: A Convolutional Architecture for Word Sequence Prediction

    作者:Mingxuan Wang.李航,刘群 单位:华为.中科院 时间:2015 发表于:acl 2015 文章下载:http://pan.baidu.com/s/1bnBBVuJ 主要内容: 用de ...

  8. Effective JavaScript Item 36 实例状态仅仅保存在实例对象上

    本系列作为EffectiveJavaScript的读书笔记. 一个类型的prototype和该类型的实例之间是"一对多"的关系.那么,须要确保实例相关的数据不会被错误地保存在pro ...

  9. 4.跟我学solr---SolrRequestHandler具体解释

    概述 我们在使用solr admin在做查询的时候,能够看到Request-Hander(qt)输入栏中有"/select"这样一个uri.当我们点击查询的时候所发起的请求是这种. ...

  10. 列表的实现-----数据结构与算法JavaScript描述 第三章

    实现一个列表 script var booklist = new List(); booklist.append('jsbook'); booklist.append('cssbook'); book ...