You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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更好?

  • 自动处理自我赋值:因为传值的时候已经拷贝了一份,交换不会有问题
  • 异常安全:如果拷贝构造抛出异常,原对象的状态完全不受影响
  • 代码简洁,不用写一堆判断逻辑

总结一下

  1. 赋值运算符只负责给已存在的对象赋值,不会创建新实例;创建新实例用拷贝构造函数
  2. this指针在合法成员函数调用中永远非空,不用做this == NULL的判断
  3. 拷贝构造和赋值运算符要遵循三/五法则,用拷贝交换Idiom能简化实现

内容的提问来源于stack exchange,提问作者DrHankPym

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:10:56