C++拷贝赋值运算符成员删除/重置作用及版本正确性问询
class B; class A { private: list<B*> myList; unique_ptr<B> smartUniq; public: A() { myList.push_back(new B(5)); myList.push_back(new B(6)); myList.push_back(new B(7)); } ~A() { for (auto& item : myList) { delete item; } cout << "destructor"<<endl; } //Copy constructor A(const A &src) { for (auto& item : src.myList) { myList.push_back(new B(item->getb())); } smartUniq(make_unique<B>(*src.smartUniq)) } ///////////////////////// VERSION1: Copy assignement A& operator= (const A &src) { for (auto& item : myList) { if (item != nullptr) delete item; } for (auto& item : src.myList) { myList.push_back(new B(item->getb())); } smartUniq(make_unique<B>(*src.smartUniq)) return *this; } ///////////////////////// VERSION2: Copy assignement A& operator= (const A &src) { if( this != &src) { for (auto& item : src.myList) { myList.push_back(new B(item->getb())); } smartUniq.reset(src.smartUniq.get() ? make_unique<B>(*src.smartUniq) : nullptr); } return *this; } };
问题
Q1:下述两个拷贝赋值运算符版本(Version1、Version2或其他)哪个正确?
- 拷贝前删除当前成员的作用是什么?
- 代码中
if( this != &src)的作用是什么?
Q2:smartUniq.reset的作用是什么?是否可以像Version1那样不使用该方法?
回答
Q1 分析
哪个版本正确?
两个版本都不正确,各自存在严重问题:
- Version1的问题:
- 删除
myList元素后没有清空列表,导致列表里全是野指针,后续析构函数会重复delete这些野指针,触发未定义行为; smartUniq(make_unique<B>(*src.smartUniq))是语法错误,unique_ptr不能直接这么调用构造赋值;- 未处理
src.smartUniq为空的情况,直接解引用会触发空指针访问。
- 删除
- Version2的问题:
- 没有先释放当前
myList的已有元素,直接push_back新元素,导致原有内存完全泄漏; - 即使加了自赋值判断,正常赋值场景下的内存泄漏问题依然存在。
- 没有先释放当前
正确的拷贝赋值应该先完全清理当前对象的资源(delete所有myList元素并清空列表),再拷贝源对象的资源,同时处理自赋值和异常安全(比如用copy-and-swap惯用法)。
拷贝前删除当前成员的作用
释放当前对象已经占用的动态内存,避免内存泄漏。如果不删除原有成员就直接添加新内容,原来在堆上分配的B对象会因为失去指针引用而无法被释放,最终造成内存泄漏。
if( this != &src)的作用
防止自赋值(比如执行a = a;这种操作)。如果没有这个判断,自赋值时会先删除当前对象的资源,之后再从已被销毁的资源里拷贝,导致访问野指针,触发未定义行为。
Q2 分析
smartUniq.reset的作用
unique_ptr::reset()有两个核心功能:
- 释放当前
unique_ptr指向的对象(如果当前有指向的对象); - 将
unique_ptr重新指向传入的新对象(如果传入了参数),如果不传参数就把unique_ptr置为空。
Version2里的写法还处理了src.smartUniq为空的情况,避免空指针解引用。
是否可以像Version1那样不使用reset?
可以,但要修正Version1里的错误写法:
- Version1里的
smartUniq(make_unique<B>(*src.smartUniq))是语法错误,正确的写法应该用赋值操作:smartUniq = make_unique<B>(*src.smartUniq); - 但要注意,这种写法需要先判断
src.smartUniq是否为空,否则直接解引用空指针会触发未定义行为,比如:if (src.smartUniq) { smartUniq = make_unique<B>(*src.smartUniq); } else { smartUniq.reset(); // 等价于smartUniq = nullptr; }
unique_ptr的赋值运算符会自动释放原有对象,所以效果和用reset()类似,但reset()的写法在处理空指针场景时更简洁直观。
内容的提问来源于stack exchange,提问作者Ghazi
相关产品推荐
相关产品推荐

