当前位置: 首页 > news >正文

C++拷贝构造函数详解:从浅拷贝到深拷贝

什么是拷贝构造函数?

拷贝构造函数是C++中的一种特殊构造函数,用于创建一个新对象作为现有对象的副本。当我们使用一个对象来初始化同类型的另一个对象时,拷贝构造函数就会被调用。

基本语法

class MyClass {
public:// 拷贝构造函数MyClass(const MyClass& other) {// 拷贝操作}
};

何时调用拷贝构造函数?

拷贝构造函数在以下情况下被调用:

  1. 对象初始化

    MyClass obj1;
    MyClass obj2 = obj1; // 拷贝构造函数被调用
    MyClass obj3(obj1);  // 拷贝构造函数被调用
    
  2. 函数参数传递(按值传递):

    void doSomething(MyClass obj) {// 函数体
    }MyClass myObj;
    doSomething(myObj); // 拷贝构造函数被调用
    
  3. 函数返回值(按值返回):

    MyClass createObject() {MyClass obj;return obj; // 可能调用拷贝构造函数(取决于编译器优化)
    }
    

默认拷贝构造函数的问题:浅拷贝

如果你没有自定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。这个默认实现执行浅拷贝(成员逐一复制),对于简单类型没问题,但对于指针成员会导致问题:

class ShallowCopy {
private:int* data;int size;
public:ShallowCopy(int sz) : size(sz) {data = new int[size];}// 默认拷贝构造函数执行浅拷贝:// data = other.data; (复制指针值,而不是指向的内容)// size = other.size;~ShallowCopy() {delete[] data;}
};

使用这个类会导致双重释放问题:

ShallowCopy obj1(10);
ShallowCopy obj2 = obj1; // 浅拷贝:两个对象的data指向同一内存
// obj1和obj2析构时都会尝试释放同一块内存 → 崩溃!

解决方案:深拷贝

为了解决浅拷贝的问题,我们需要自定义拷贝构造函数来实现深拷贝

class DeepCopy {
private:int* data;int size;
public:DeepCopy(int sz) : size(sz) {data = new int[size];}// 自定义拷贝构造函数(深拷贝)DeepCopy(const DeepCopy& other) : size(other.size) {data = new int[size];for (int i = 0; i < size; ++i) {data[i] = other.data[i]; // 复制内容,而不是指针}}~DeepCopy() {delete[] data;}
};

完整的示例代码

#include <iostream>
#include <cstring>class String {
private:char* str;int length;public:// 普通构造函数String(const char* s = "") {length = std::strlen(s);str = new char[length + 1];std::strcpy(str, s);}// 拷贝构造函数(深拷贝)String(const String& other) : length(other.length) {str = new char[length + 1];std::strcpy(str, other.str);std::cout << "拷贝构造函数被调用" << std::endl;}// 析构函数~String() {delete[] str;}// 显示字符串void display() const {std::cout << str << std::endl;}// 修改字符串void update(const char* s) {delete[] str;length = std::strlen(s);str = new char[length + 1];std::strcpy(str, s);}
};int main() {String s1("Hello");String s2 = s1; // 拷贝构造函数被调用std::cout << "s1: ";s1.display();std::cout << "s2: ";s2.display();s1.update("World"); // 修改s1,不影响s2std::cout << "修改后:" << std::endl;std::cout << "s1: ";s1.display();std::cout << "s2: ";s2.display();return 0;
}

拷贝构造函数与赋值运算符的区别

初学者常常混淆拷贝构造函数和赋值运算符,它们的区别在于:

  • 拷贝构造函数:创建新对象时使用
  • 赋值运算符:已存在的对象被赋予新值时使用
MyClass obj1;
MyClass obj2 = obj1; // 拷贝构造函数
MyClass obj3;
obj3 = obj1;         // 赋值运算符

禁止拷贝

有时候我们想禁止对象的拷贝行为,C++11之前可以通过将拷贝构造函数声明为private来实现,C++11之后可以使用delete关键字:

class NonCopyable {
public:NonCopyable() = default;// 禁止拷贝NonCopyable(const NonCopyable&) = delete;NonCopyable& operator=(const NonCopyable&) = delete;
};

最佳实践

  1. 三分法则:如果你需要自定义析构函数、拷贝构造函数或拷贝赋值运算符中的任何一个,你可能需要自定义所有这三个。

  2. 考虑使用智能指针:现代C++中,使用std::unique_ptrstd::shared_ptr可以避免许多手动内存管理的问题。

  3. 优先使用const引用传递对象:避免不必要的拷贝,提高性能。

  4. 考虑移动语义:C++11引入了移动构造函数和移动赋值运算符,对于管理资源的类,实现移动语义可以显著提高性能。

总结

拷贝构造函数是C++中管理对象复制行为的重要工具。理解浅拷贝和深拷贝的区别对于编写正确、安全的C++代码至关重要。对于包含动态分配资源的类,几乎总是需要自定义拷贝构造函数来实现深拷贝,避免潜在的内存问题和未定义行为。

在现代C++开发中,随着智能指针和移动语义的引入,我们需要手动实现拷贝构造函数的情况减少了,但理解其原理仍然非常重要。

http://www.wxhsa.cn/company.asp?id=5808

相关文章:

  • ThreadLocal
  • K8S探针
  • 模拟赛
  • bug1
  • C#第十二天 025
  • 选择语句的机器级表示
  • pip常用命令
  • 我的大学规划
  • 深入解析:numpy学习笔记
  • 理解 Linux 系统中的熵(Entropy)
  • Nginx auth_request 模块使用
  • 用nssm将minio和srs注册成服务
  • Mac上的Markdown学习
  • ubuntu 18.04安装mysql8.4.5
  • Radxa E20C 安装 OpenWrt
  • 第三篇:配置浏览器
  • 第二篇:playwright初步解析
  • 高性能计算-TensorCore-hgemm
  • 第一篇:Playwright-Python安装与调试
  • P13695 [CEOI 2025] theseus 题解
  • 《ESP32-S3使用指南—IDF版 V1.6》第三十八章 SPIFFS实验
  • 技术交流社区基础防诈指南
  • 神秘题
  • 技术群高级防骗指南
  • 集训游记
  • SQL Server 中的 STUFF 函数与FOR XML PATH详解 - 实践
  • 2025/9/16 总结
  • Linux备份数据
  • np.argmax
  • TQ322数字PIR使用笔记