剑指offer 2.2 - 赋值运算函数
来源:互联网 发布:网络说唱歌曲 编辑:程序博客网 时间:2024/06/05 19:59
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
- 剑指offer 2.2 - 赋值运算函数
- 【剑指offer】赋值运算符函数_01
- 【剑指Offer】赋值运算符函数
- 【剑指offer】1.赋值运算符函数
- 剑指offer - 赋值运算符函数
- 《剑指offer》01 赋值运算符函数
- 剑指offer--赋值运算符函数
- 剑指offer-01:赋值运算符函数
- 剑指Offer算法实现之一:赋值运算符函数
- 剑指Offer:面试题01---赋值运算符函数
- 剑指offer面试题(一):赋值运算符函数
- 剑指offer:面试题一:赋值运算符函数
- 《剑指Offer》面试题1:赋值运算符函数
- 剑指Offer系列---(3)赋值运算符函数
- 剑指offer:面试题1 赋值运算符函数
- 剑指offer,面试题一:赋值运算符函数
- 《剑指offer》——重载赋值运算符函数
- 剑指offer-面试题1:赋值运算符函数
- CodeForces 81A
- android.view.ViewGroup.LayoutParams介绍
- Android实现关机/重启
- Shell脚本实现数据父子关系定义——目标:将目标数据批量导入MySQL数据库
- 一点感想
- 剑指offer 2.2 - 赋值运算函数
- UVa-401-Palindromes
- UVA - 10324 Zeros and Ones
- LeetCode-Set Matrix Zeroes
- 栈的原地翻转
- Java实现二叉树的创建、递归/非递归遍历
- 南阳 312
- C/C++ 下的void main()
- 设计模式六大原则(4):接口隔离原则