导读:本期聚焦于小伙伴创作的《C++深拷贝与浅拷贝的区别是什么?拷贝构造函数编写规范有哪些》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++深拷贝与浅拷贝的区别是什么?拷贝构造函数编写规范有哪些》有用,将其分享出去将是对创作者最好的鼓励。

在C++面向对象编程中,对象的拷贝操作是非常常见的场景,而深拷贝和浅拷贝是实现对象拷贝的两种不同方式,二者的核心差异在于是否对对象内部的动态资源进行独立的复制。理解二者的区别并掌握拷贝构造函数的编写规范,是写出正确C++程序的基础。

C++深拷贝与浅拷贝的区别是什么?拷贝构造函数编写规范有哪些

浅拷贝的定义与特点

浅拷贝指的是在拷贝对象时,仅复制对象本身的非静态成员变量的值,不会复制成员变量指向的动态分配资源。如果对象内部包含指针类型的成员,浅拷贝只会复制指针的值,也就是让新对象的指针和原对象的指针指向同一块内存区域。

当类中没有显式定义拷贝构造函数时,编译器会自动生成一个默认的拷贝构造函数,这个默认的构造函数执行的就是浅拷贝操作。下面是一个浅拷贝的示例:

#include <iostream>
#include <cstring>

class ShallowCopyDemo {
public:
    char* data;
    // 构造函数,动态分配内存
    ShallowCopyDemo(const char* str) {
        data = new char[strlen(str) + 1];
        strcpy(data, str);
    }
    // 析构函数,释放动态内存
    ~ShallowCopyDemo() {
        delete[] data;
    }
};

int main() {
    ShallowCopyDemo obj1("hello");
    // 使用默认拷贝构造函数,执行浅拷贝
    ShallowCopyDemo obj2 = obj1;
    std::cout << "obj1 data address: " << (void*)obj1.data << std::endl;
    std::cout << "obj2 data address: " << (void*)obj2.data << std::endl;
    return 0;
}

运行上述代码会发现,obj1和obj2的data指针指向的是同一块内存地址。当程序结束时,obj1和obj2的析构函数都会被调用,这会导致同一块内存被释放两次,触发程序崩溃。

深拷贝的定义与特点

深拷贝则是在拷贝对象时,不仅复制对象本身的非静态成员变量的值,还会为对象内部的动态资源重新分配内存,并将原资源的内容复制到新分配的内存中。这样新对象和原对象的动态资源是相互独立的两块内存,修改其中一个不会影响另一个,析构时也不会出现重复释放的问题。

要实现深拷贝,需要显式定义拷贝构造函数,在构造函数中为指针成员重新分配内存并复制内容。下面是深拷贝的示例:

#include <iostream>
#include <cstring>

class DeepCopyDemo {
public:
    char* data;
    // 构造函数,动态分配内存
    DeepCopyDemo(const char* str) {
        data = new char[strlen(str) + 1];
        strcpy(data, str);
    }
    // 显式定义拷贝构造函数,实现深拷贝
    DeepCopyDemo(const DeepCopyDemo& other) {
        data = new char[strlen(other.data) + 1];
        strcpy(data, other.data);
    }
    // 析构函数,释放动态内存
    ~DeepCopyDemo() {
        delete[] data;
    }
};

int main() {
    DeepCopyDemo obj1("hello");
    // 使用自定义拷贝构造函数,执行深拷贝
    DeepCopyDemo obj2 = obj1;
    std::cout << "obj1 data address: " << (void*)obj1.data << std::endl;
    std::cout << "obj2 data address: " << (void*)obj2.data << std::endl;
    return 0;
}

运行上述代码可以看到,obj1和obj2的data指针指向不同的内存地址,二者析构时各自释放自己的内存,不会出现重复释放的问题。

深拷贝与浅拷贝的核心区别

二者的核心差异可以总结为以下几点:

  • 资源复制方式不同:浅拷贝仅复制指针值,新对象和原对象共享动态资源;深拷贝会重新分配资源并复制内容,新对象和原对象拥有独立的动态资源。
  • 适用场景不同:如果类中没有动态分配的资源,或者明确需要多个对象共享同一份资源,可以使用浅拷贝;如果类包含动态分配的资源,且需要每个对象独立管理自己的资源,必须使用深拷贝。
  • 潜在问题不同:浅拷贝容易导致重复释放内存、野指针等问题;深拷贝如果实现不当可能导致内存泄漏,但不会出现重复释放的问题。

C++拷贝构造函数的编写规范

编写拷贝构造函数时,需要遵循以下规范,避免出现错误:

1. 函数签名规范

拷贝构造函数的第一个参数必须是自身类型的const引用,通常格式为类名(const 类名& other)。使用const是为了保证在拷贝过程中不会修改原对象的内容,使用引用是为了避免拷贝构造函数调用时产生无限递归(如果参数是值传递,会再次触发拷贝构造函数的调用)。

2. 成员初始化顺序规范

拷贝构造函数的成员初始化顺序需要和类成员声明的顺序保持一致,避免因为初始化顺序错误导致的问题。对于需要深拷贝的动态资源成员,需要在初始化列表中或者构造函数体内重新分配内存并复制内容。

3. 处理动态资源的规范

如果类包含动态分配的资源(如new分配的内存、打开的文件句柄等),必须在拷贝构造函数中为这些资源重新分配独立的副本,实现深拷贝。如果类不需要深拷贝,也需要显式声明拷贝构造函数为删除状态,避免编译器生成默认的浅拷贝构造函数,格式为类名(const 类名& other) = delete;

4. 拷贝赋值运算符的配套实现

如果显式定义了拷贝构造函数,通常也需要配套定义拷贝赋值运算符,遵循同样的深拷贝或浅拷贝逻辑,避免出现拷贝构造和赋值操作行为不一致的问题。拷贝赋值运算符的格式通常为类名& operator=(const 类名& other)

完整的拷贝构造函数实现示例

下面是一个包含动态资源、遵循编写规范的类的完整实现:

#include <iostream>
#include <cstring>

class Student {
private:
    char* name;
    int age;
public:
    // 普通构造函数
    Student(const char* n, int a) : age(a) {
        name = new char[strlen(n) + 1];
        strcpy(name, n);
    }
    // 拷贝构造函数,遵循规范实现深拷贝
    Student(const Student& other) : age(other.age) {
        name = new char[strlen(other.name) + 1];
        strcpy(name, other.name);
    }
    // 拷贝赋值运算符,配套实现深拷贝
    Student& operator=(const Student& other) {
        if (this == &other) {
            return *this;
        }
        // 先释放原有资源
        delete[] name;
        // 复制新资源
        age = other.age;
        name = new char[strlen(other.name) + 1];
        strcpy(name, other.name);
        return *this;
    }
    // 析构函数
    ~Student() {
        delete[] name;
    }
    // 打印信息的函数
    void printInfo() const {
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    }
};

int main() {
    Student s1("张三", 20);
    Student s2 = s1; // 调用拷贝构造函数
    Student s3("李四", 22);
    s3 = s1; // 调用拷贝赋值运算符
    s1.printInfo();
    s2.printInfo();
    s3.printInfo();
    return 0;
}

上述代码中的Student类显式定义了拷贝构造函数和拷贝赋值运算符,都实现了深拷贝逻辑,同时处理了自赋值的情况,是符合编写规范的实现。

常见注意事项

在实际开发中,还需要注意以下几点:

  • 如果类继承了其他类,拷贝构造函数需要调用父类的拷贝构造函数,确保父类的成员也被正确拷贝。
  • 如果类包含const成员或者引用成员,这些成员必须在初始化列表中初始化,不能在构造函数体内赋值,因此拷贝构造函数也需要通过初始化列表来初始化这些成员。
  • 使用std::stringstd::vector等标准库容器作为成员时,不需要手动实现深拷贝,因为这些容器本身已经实现了正确的拷贝逻辑,编译器生成的默认拷贝构造函数就会正确拷贝这些成员。

C++深拷贝浅拷贝拷贝构造函数修改时间:2026-06-26 01:42:45

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。