C++默认拷贝构造函数行为不符预期的原因咨询
嘿,你推理里的误区其实出在对p2.name = "Bill";这个操作的理解上,咱们一步步拆解来搞懂为什么实际输出和你预期不一样:
1. 默认拷贝构造函数的行为(浅拷贝)
你说的没错,C++的默认拷贝构造函数确实是浅拷贝:创建p2时,会把p1的每个成员变量的值直接拷贝给p2。所以一开始p1.name和p2.name指向的是同一块堆内存——就是p1构造时用new char[]分配的、存储着"Tom"的那块内存。
2. 为什么修改p2.name没影响p1.name?
关键就看这行代码:
p2.name = "Bill";
你以为这是在修改p2.name指向的内存内容,但实际上,这行代码做的是重新赋值指针本身:
"Bill"是一个字符串常量,它存储在程序的静态存储区,有自己独立的内存地址。- 这行代码把
p2.name这个指针变量的值,从原来指向堆内存的地址,改成了指向"Bill"这个常量的地址。
而p1.name的指针值并没有被改变,它仍然指向原来那块存储着"Tom"的堆内存,所以当你输出p1.name时,自然会打印"Tom"。
3. 如何实现你预期的效果?
如果你想让修改p2的name同时影响p1,你需要修改指针指向的内存内容,而不是修改指针本身。比如用strcpy函数:
strcpy(p2.name, "Bill");
这样就会把"Bill"复制到p2.name(也就是p1.name)指向的堆内存里,此时输出p1.name就会得到"Bill"。不过要注意:原来的堆内存只分配了strlen("Tom")+1=4字节的空间,"Bill"刚好也是4字节(包含末尾的'\0'),不会越界;如果改成更长的字符串,就会导致内存越界,触发未定义行为。
额外提醒:浅拷贝的隐藏隐患
你的代码还有一个容易忽略的问题:当p1和p2生命周期结束时,它们的析构函数都会调用delete[] name,也就是对同一块堆内存释放两次,这属于双重释放,会触发未定义行为(比如程序崩溃、异常等)。解决这个问题的方法是自己实现深拷贝构造函数,在拷贝时为name重新分配内存并复制内容。
内容的提问来源于stack exchange,提问作者jaayy

