C++拷贝赋值运算符:空对象时能否创建新实例?
关于C++拷贝赋值运算符与空对象的疑问解答
嘿,咱们先把核心问题掰明白——首先你得知道:在合法的成员函数调用中,this指针永远不会是NULL。你代码里写的if (this == NULL)完全是多余的,因为赋值运算符operator=只能被已经存在的对象调用,空指针根本没法直接触发成员函数调用(强行通过空指针调用属于未定义行为,编译器不会保证任何结果)。
你可能混淆了两个场景:
你说的“对象为空时创建新实例”,应该是指这种情况:
Person* p = nullptr; // 你想让p指向一个新的Person实例,复制另一个对象的内容
但这根本不是赋值运算符的工作——赋值运算符是给已经存在的对象赋值,而不是创建新对象。这种场景下你应该直接用拷贝构造函数创建新对象:
p = new Person(existingPerson);
你的Person类代码存在的问题
先看你的拷贝构造函数:
Person(const Person& rhs) { if (pName_ != NULL) delete pName_; pName_ = new string(*(rhs.pName())); }
这里有个致命错误:刚创建的对象的pName_是未初始化的野指针,pName_ != NULL的判断完全无效,可能会触发非法内存访问。正确的拷贝构造应该用初始化列表直接初始化指针:
Person(const Person& rhs) : pName_(new string(*rhs.pName_)) {}
(假设你有pName()成员函数是返回pName_的,那改成*(rhs.pName())也可以)
再看你的拷贝赋值运算符,除了多余的this == NULL判断,还存在自我赋值的风险(比如a = a;),而且写法不够简洁安全。推荐用拷贝交换Idiom来实现,既避免自我赋值问题,又能保证异常安全:
修正后的完整Person类
#include <string> #include <iostream> using namespace std; class Person { private: string* pName_; // 假设这是你的私有成员 public: Person(string name) : pName_(new string(name)) {} ~Person() { delete pName_; } // 正确的拷贝构造函数 Person(const Person& rhs) : pName_(new string(*rhs.pName_)) {} // 拷贝交换Idiom实现赋值运算符 Person& operator=(Person rhs) { // 这里传值,自动调用拷贝构造 swap(pName_, rhs.pName_); // 交换当前对象和临时对象的指针 return *this; // 临时对象销毁时会自动释放原来的内存 } // 辅助函数:返回pName_ const string* pName() const { return pName_; } };
为什么拷贝交换Idiom更好?
- 自动处理自我赋值:因为传值的时候已经拷贝了一份,交换不会有问题
- 异常安全:如果拷贝构造抛出异常,原对象的状态完全不受影响
- 代码简洁,不用写一堆判断逻辑
总结一下
- 赋值运算符只负责给已存在的对象赋值,不会创建新实例;创建新实例用拷贝构造函数
this指针在合法成员函数调用中永远非空,不用做this == NULL的判断- 拷贝构造和赋值运算符要遵循三/五法则,用拷贝交换Idiom能简化实现
内容的提问来源于stack exchange,提问作者DrHankPym
相关产品推荐
相关产品推荐

