C++拷贝构造函数未将字符复制到新动态内存位置原因咨询
C++ char*成员深拷贝异常问题分析
问题描述
在实现带char*类型成员的类的拷贝构造函数时,已显式为新对象的指针成员分配独立动态内存,但运行时观测到新旧对象的指针成员仍指向同一内存地址,修改其中一个对象的成员值会同步修改另一个对象的对应值,未达到深拷贝预期。
问题复现代码如下:
#include <iostream> class Test{ public: char *c; Test(char a){ c = new char(); *c = a; } Test (const Test& obj){ c = new char(); this->c = obj.c; // 该行未达到预期效果 } char getValue(){ return *c; } void setValue(char a){ *c = a; } char* getAddress(){ std::cout << "Address is " << c << std::endl; } }; int main() { Test t1 ('a'); Test t2 = t1; std::cout << "t1.c " << t1.getValue() << std::endl; std::cout << "t2.c " << t2.getValue() << std::endl; t1.getAddress(); t2.getAddress(); t2.setValue('z'); std::cout << "t1.c " << t1.getValue() << std::endl; std::cout << "t2.c " << t2.getValue() << std::endl; t1.getAddress(); t2.getAddress(); return 0; }
将拷贝构造函数内的赋值逻辑替换为strcpy后,可正常实现深拷贝,对应实现如下:
Test (const Test& obj){ c = new char(); strcpy(this->c, obj.c); }
根本原因
错误的核心是混淆了指针本身的赋值和指针指向内容的赋值:
- 拷贝构造函数中第一行
c = new char();确实为新对象申请了一块独立的堆内存,此时新对象的c指针存储的是这块新内存的起始地址。 - 紧接着执行的
this->c = obj.c;是指针变量的直接赋值:这行代码会把源对象obj.c存储的地址值,直接覆盖掉this->c里原本保存的新申请内存的地址。
这行代码执行后会产生两个结果:- 刚申请的新堆内存没有任何指针指向,直接发生内存泄漏
- 新对象的
c和源对象的c存储了完全相同的地址,二者指向同一块堆内存,本质还是浅拷贝,修改其中一个指针指向的内容自然会影响另一个对象。
strcpy实现深拷贝的原理
strcpy操作的是指针指向的内存空间内容,不会修改指针本身存储的地址值:
- 第一行
c = new char();申请的新内存地址会一直保存在this->c中,不会被覆盖 strcpy会把源对象c指向内存中存储的字符数据,拷贝到新对象c指向的新分配内存中。此时两个指针分别指向两块独立的内存,只是内存中存储的内容一致,完全符合深拷贝的预期。
额外说明:示例代码里的
getAddress函数存在实现缺陷:直接给cout传入char*指针时,标准库会将其识别为C风格字符串逐字符打印,由于代码只分配了1字节的char空间、没有预留字符串终止符\0的位置,会触发内存越界访问;同时该函数声明返回值为char*但没有写return语句,属于未定义行为。如果要打印指针保存的内存地址,需要把c强制转换为void*类型再输出。
内容的提问来源于stack exchange,提问作者Jayakumar
相关产品推荐
相关产品推荐

