5.通过拷贝构造函数复制一个对象,假如对象的成员中有个指针类型的变量,如何避免拷贝出来的副本中的该成员之下行同一块内存(等价于默认拷贝构造函数有没有缺点)

这个问题问得非常精准,它正好切中了深拷贝和浅拷贝最核心的矛盾点。

您的问题包含两个层面:

1.默认拷贝构造函数有什么缺点?

2.如何解决这个缺点?

1. 默认拷贝构造函数的缺点(浅拷贝的陷阱)

当您不提供任何拷贝构造函数时,C++编译器会为您生成一个默认拷贝构造函数 。它的工作方式非常"天真":对对象的所有成员进行逐位复制(bit-wise copy) 。

  • 对于普通成员变量(如 int, double),这没有问题。
  • 但对于指针 成员变量,它复制的是指针的地址,而不是指针所指向的内存里的内容。

这就是浅拷贝,它会带来三个致命的缺点:

缺点一:内存重复释放 (Double Free) 这是最严重的问题。当两个对象的指针指向同一块内存时,它们各自的生命周期结束时,都会调用析构函数来 delete 这块内存。第一次 delete 成功,第二次 delete 就会作用于一块已经被释放的内存,导致程序立即崩溃。

缺点二:数据意外修改 (Unintended Side Effects) 因为两个对象共享同一份数据,通过一个对象修改了这份数据,会立刻影响到另一个对象。这破坏了对象的独立性和封装性,会导致非常隐蔽和难以调试的逻辑错误。

缺点三:悬挂指针 (Dangling Pointer) 如果其中一个对象被销-毁(比如离开了它的作用域),它会释放掉共享的内存。此时,另一个对象里的指针就变成了指向无效内存的"悬挂指针"。任何对这个指针的访问都将导致未定义行为(通常是程序崩溃)。


2. 如何避免:手动实现深拷贝

为了避免上述所有问题,我们必须手动编写自己的拷贝构造函数 ,用深拷贝来覆盖掉编译器默认的浅拷贝行为。

核心思想 :不复制地址,而是复制地址指向的内容。

具体步骤如下:

  1. 为新对象分配独立的内存:在拷贝构造函数中,为新创建的对象的指针成员使用 new 关键字,在堆上申请一块全新的、独立的内存空间。这块内存的大小应该和源对象所指向的内存大小一致。

  2. 复制内容到新内存中 :使用 strcpy、memcpy 或循环等方式,将源对象指针指向的内存中的数据内容,完整地复制到刚刚为新对象申请的新内存中。

这样,两个对象就各自拥有了位于不同地址、但内容相同的资源副本。它们互不干扰,可以独立地使用和释放,从而完美地解决了浅拷贝的所有问题。

代码示例

我们再次用 MyString 类来演示这个过程:

arduino 复制代码
class MyString {
private:
    char* p_data;
public:
    // ... 构造函数和析构函数 ...
    MyString(const char* text = "") 
    {
        p_data = new char[strlen
        (text) + 1];
        strcpy(p_data, text);
    }
    ~MyString() {
        delete[] p_data;
    }
    // 默认拷贝构造函数 (浅拷贝 - 有严重
    缺点)
    /*
    MyString(const MyString& other) 
    {
        p_data = other.p_data; // 仅
        仅复制了指针的地址,非常危险!
    }
    */
    // 我们手动实现的拷贝构造函数 (深拷
    贝 - 解决了所有问题)
    MyString(const MyString& other) 
    {
        // 步骤1:为新对象分配独立的内存
        p_data = new char[strlen
        (other.p_data) + 1];
        // 步骤2:将源对象的内容复制到新
        内存中
        strcpy(p_data, other.
        p_data);
    }
};

总结一下对面试官说的话:

"默认拷贝构造函数的缺点在于它对指针只进行浅拷贝,即只复制地址,这会导致内存重复释放、数据意外修改和悬挂指针等严重问题。

为了避免这些问题,我们必须手动实现深拷贝。具体做法是在拷贝构造函数中,不直接复制指针地址,而是为新对象重新分配一块独立的内存,然后将源对象所指向的数据内容完整地复制到这块新内存中,从而确保每个对象都拥有自己独立的资源副本。

相关推荐
现任明教教主~1 小时前
SpringBoot+Vue学生宿舍管理系统源码 带部署文档
vue.js·spring boot·后端
码事漫谈8 小时前
骂AI,会让它做得更好吗?
后端
程序喵大人8 小时前
【C++入门】编译链接模型 - 01 一个 C++ 程序是怎样变成可执行文件的
开发语言·c++·编译链接模型
陈随易8 小时前
bm2,Node.js 与 Bun 项目部署新选择
前端·后端·程序员
架构技术专栏9 小时前
AI Agent 框架怎么选:从一次制度查询拆出技术边界
后端·面试
by209999 小时前
vector 的模拟实现:扩容与边界的详细阐述(下)
c++·经验分享·vector·动态数组
明月_清风11 小时前
SaaS 的三层挣钱逻辑,正在被 AI 从第一层击穿
人工智能·后端
余额瞒着我当琳12 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
青少儿编程课堂12 小时前
AC自动机多模式串匹配精讲:校园关键词巡检实战解析
c++·python·算法·bfs·信息学竞赛
谁在黄金彼岸13 小时前
nginx服务化五套方案原理解剖与选型
后端