剑指Offer之赋值运算符函数解析
来源:互联网 发布:java图形输出的编程题 编辑:程序博客网 时间:2024/06/07 15:25
1.赋值运算符函数
AssignmentOperator
题目:如下是类型CMyString的声明,请为该类型添加赋值运算符函数
class CMyString{public: CMyString(char *p = nullptr); CMyString(const CMyString& str); ~CMyString();private: char *m_pData;}
首先对于C++一个类中的6个默认的函数分别是构造,拷贝构造,赋值运算符重载,析构,以及取地址操作符重载以及const修饰的取地址操作符重载。
对于拷贝构造以及赋值运算符重载,默认的使用是浅拷贝,也就是说将该对象的内存原封不动地挪动到新对象的内存中,因此对于含有指针的类,我们往往需要自己实现copying的操作来完成深拷贝(除非你就需要以浅拷贝的方式完成该操作),否则很有可能造成有多个指针指向同一块空间,在析构时候同一块空间析构多次导致崩溃。
常用的解决办法是重新分配内存并拷贝或者实现引用计数版本,对于此题,我们选择前者。
我给出的代码如下:
CMyString& CMyString::operator=(const CMyString& other){ if (this != &other) { delete[] m_pData; m_pData = nullptr; m_pData = new char[strlen(other.m_pData) + 1]; strcpy(m_pData, other.m_pData); } return *this;}
因为之前模拟写过String类的简单实现,所以我想了一下,就写了上面的代码。在这里,几个值得注意的点:
1. 赋值运算符的重载函数的声明,需要返回类型的引用,也就是CMyString& ,这里是为了考虑到形如 a = b = c这样的连续赋值操作,因此需要在函数结束前加上return *this;
2. 函数传参需要引用,这样避免了调用一次拷贝构造函数提高效率,同时为了不改变传入实例,需要加上const
3. 重新分配内存时候,必须要释放之前自己已有的空间,否则会导致内存泄漏
4. 要考虑到自己赋值给自己,即this == &other时候,其实不需要执行任何操作,同时更为重要的是:对于我自己写的代码如果不加上if (this != &other)代码就是错的,因为我是先释放内存再根据other需要的空间开辟一块新空间,对于自己赋值给自己的情况,由于已经自己指向的那块空间已经释放了,所以再也找不到需要赋值的内容了。
写完对照书发现和书上大差不离。。嗯,适合初级程序员,不过想了想我这现在水平估计还不如初级呢(一脸懵逼,哈哈哈,下面看看书上是怎么给出了考虑异常安全性的的解法
对于我之前写的代码,我是先释放之前的内存再开辟新空间,如果此时内存不足导致new时抛出异常,那么此时m_pData已经为空指针,容易导致程序崩溃,这样违背了异常安全性(Exception Safety)的原则,因此可以采用先分配新空间,分配成功后再释放原来的内容,当然书上给出了一个更好的方法,先创建一个临时实例,再交换临时实例和原来的实例。代码如下:
CMyString& CMyString::operator=(const CMyString& other){ if (this != &other) { //先分配空间 char *pTemp = new char[strlen(other.m_pData) + 1]; strcpy(pTemp, other.m_pData); //分配成功后再释放原来的内存 delete[] m_pData; m_pData = nullptr; m_pData = pTemp; } return *this;}
更好的办法:
CMyString& CMyString::operator=(const CMyString& other){ if (this != &other) { CMyString temp(other); swap(temp.m_pData, m_pData); } return *this;}
在这个函数中,我们创建了一个临时对象temp,然后交换了temp.m_pData和m_pData指向的空间,此时temp指向的空间即为m_pData之前的空间,由于temp是个局部对象,运行到if作用域外,就用自动调用temp的析构函数从而完成了内存的释放,同时也完成了相应的拷贝工作。代码也简洁的多,确实是个更好的办法。当然我觉得,代码可以在此基础上更简便一点:
CMyString& CMyString::operator=(CMyString other){ swap(other.m_pData, m_pData); return *this;}
这样写需要改变参数的传参,传值而非引用,由于传值时这个参数仅仅只是调用者的一份拷贝(不用考虑自己给自己的情况),相当于上面创建临时对象,此时交换析构原理和上面基本类似不再赘述。
- 剑指Offer之赋值运算符函数解析
- 剑指offer面试题1之赋值运算符函数
- 剑指Offer之面试题1:赋值运算符函数
- 《剑指Offer》面试题一之赋值运算符函数
- 剑指offer面试题之赋值运算符函数
- 【剑指offer】赋值运算符函数_01
- 【剑指Offer】赋值运算符函数
- 【剑指offer】1.赋值运算符函数
- 剑指offer - 赋值运算符函数
- 《剑指offer》01 赋值运算符函数
- 剑指offer--赋值运算符函数
- 剑指offer-01:赋值运算符函数
- 剑指offer 赋值运算符
- 剑指Offer算法实现之一:赋值运算符函数
- 剑指Offer:面试题01---赋值运算符函数
- 剑指offer面试题(一):赋值运算符函数
- 剑指offer:面试题一:赋值运算符函数
- 《剑指Offer》面试题1:赋值运算符函数
- 数据库程序
- 最大公约数&&最小公倍数C语言
- 事务特性和隔离级别
- [Java]读取文件方法大全
- 图像拼接(三):OpenCV同时打开两个摄像头捕获视频
- 剑指Offer之赋值运算符函数解析
- Hibernate-总结
- 组合与聚合
- http请求方法
- noip2016 Day2 T2-组合数问题-组合数学-前缀和
- C#判断一个String是否为数字类型
- ZOJ 3175 Number of Containers(n/1+n/2+...+n/(n-1)+n/n求法)
- HDU5690: All X(大数取模,同余)
- 安卓中@Nullable和NotNull 注释