You need to enable JavaScript to run this app. 咨询C++类浅拷贝行为、拷贝初始化形式差异及指针解引用赋值原理
优惠活动
大模型
产品
解决方案
定价
更多

咨询C++类浅拷贝行为、拷贝初始化形式差异及指针解引用赋值原理

代码运行逻辑与相关疑问解答

参考代码

class A {
public:
    int* x;
};

class B {
public:
    A _a;
};

int main()
{
    B b1;
    b1._a.x = new int(1);
    B b2(b1);
    *b2._a.x = 2;
    std::cout << *(b1._a.x) << std::endl;
}

现有理解的校正与补全

你对最终输出结果的因果推导方向是正确的,但存在一处关键概念偏差,也遗漏了C++默认拷贝语义的核心原理:

  • 错误认知:“b1与b2的_a是同一个对象”。实际上b1和b2是两个完全独立的B类实例,二者内嵌的_a成员也是各自独立的A类对象,自始至终都不是同一个对象。
  • 真正导致两个对象的指针指向同一块内存的核心原因:当类没有自定义拷贝构造函数时,C++会自动生成默认的逐成员拷贝构造函数,拷贝过程按值逐字节复制所有成员的内存内容:
    1. 拷贝B类对象时,会将b1的所有成员原样复制到b2的内存空间,其中就包括b1内嵌的A类_a成员的全部内容
    2. 对A类的int* x指针成员来说,逐字节复制的结果就是b2._a.x拿到了和b1._a.x完全相同的指针值——也就是两个独立A对象里的两个独立指针变量,恰好存储了同一个堆内存的地址
  • 后续修改这块公共堆内存的数值时,不管通过b1的x还是b2的x访问,读写的都是同一块内存空间,因此最终打印结果为2。这种逐字节拷贝带来的指针共享问题,就是常说的浅拷贝陷阱。

问题1:B b2(b1)与B b2 = b1是否始终等价?

  • 同类型对象的普通拷贝场景下,两种写法的最终行为基本一致,B b2 = b1本质上就是C++拷贝初始化的标准语法形式,语义上就是通过拷贝构造函数从b1生成b2。
  • 明确存在不等价的场景:
    1. 当类的拷贝构造函数被explicit修饰时:B b2(b1)是直接初始化写法,可以正常调用explicit拷贝构造函数;但B b2 = b1是拷贝初始化写法,会拒绝调用explicit修饰的构造函数,直接触发编译错误。
    2. 当赋值两侧类型不一致时:比如类定义了从其他类型到B的隐式转换构造函数,B b2 = 其他类型实例会先生成临时B对象,再将临时对象拷贝给b2;而B b2(其他类型实例)会直接调用对应构造函数生成b2,不会产生临时对象。

问题2:为什么*b2._a.x = 2可以直接对解引用结果赋值?是否和引用有关?

  • 该操作的可行性完全由解引用运算符的固有语义决定,和引用的关联来自运算符本身的返回值设计:
    1. C++规定,对类型为T*的指针执行*p解引用操作时,返回值类型是指向对象的左值引用T&
    2. 左值的定义是拥有稳定内存地址、可以出现在赋值号左侧的表达式,本身就支持赋值操作
    3. 对int*类型的b2._a.x做解引用时,返回的是它指向的堆上int变量的左值引用,自然可以直接作为赋值操作的左操作数,将2写入对应的内存地址。
  • 解引用返回引用类型是运算符的固有设计,不是赋值操作必须依赖引用才能完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 13:24:02