温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C++中的拷贝构造是怎样的

发布时间:2022-01-17 13:35:43 来源:亿速云 阅读:171 作者:柒染 栏目:开发技术
# C++中的拷贝构造是怎样的 ## 1. 拷贝构造函数的基本概念 ### 1.1 什么是拷贝构造函数 拷贝构造函数是C++中一种特殊的构造函数,它用于**创建一个新对象作为现有对象的副本**。当我们需要用一个已存在的对象初始化同类型的新对象时,拷贝构造函数就会被调用。 ```cpp class MyClass { public: // 拷贝构造函数声明 MyClass(const MyClass& other); }; 

1.2 拷贝构造函数的特征

  • 函数名与类名相同
  • 参数是对同类对象的常量引用(通常为const T&)
  • 没有返回值(包括void)
  • 通常是public访问权限

1.3 何时会调用拷贝构造函数

拷贝构造函数在以下场景会被自动调用:

  1. 显式初始化:用已有对象初始化新对象

    MyClass obj1; MyClass obj2 = obj1; // 调用拷贝构造函数 
  2. 函数参数传递:对象作为函数参数按值传递时 “`cpp void func(MyClass obj);

MyClass original; func(original); // 调用拷贝构造函数

 3. **函数返回对象**:函数返回对象时(可能被编译器优化) ```cpp MyClass createObject() { MyClass obj; return obj; // 可能调用拷贝构造函数 } 

2. 默认拷贝构造函数

2.1 编译器生成的默认拷贝构造函数

如果类没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。这个默认实现会执行浅拷贝(shallow copy),即:

  • 对基本类型成员:直接复制值
  • 对类类型成员:调用该成员的拷贝构造函数
  • 对指针成员:仅复制指针值(不复制指向的内容)

2.2 默认拷贝构造函数的局限性

当类包含动态分配的资源(如堆内存、文件句柄等)时,默认拷贝构造函数会导致问题:

class ProblemClass { public: int* data; ProblemClass(int size) { data = new int[size]; } ~ProblemClass() { delete[] data; } // 没有自定义拷贝构造函数 }; void demo() { ProblemClass obj1(10); ProblemClass obj2 = obj1; // 浅拷贝,两个对象指向同一内存 // 析构时会导致双重释放错误! } 

3. 自定义拷贝构造函数

3.1 实现深拷贝

对于需要管理资源的类,必须自定义拷贝构造函数实现深拷贝(deep copy):

class SafeArray { public: int* ptr; int size; // 自定义拷贝构造函数 SafeArray(const SafeArray& other) : size(other.size) { ptr = new int[size]; for(int i = 0; i < size; ++i) { ptr[i] = other.ptr[i]; } } // 构造函数 SafeArray(int s) : size(s) { ptr = new int[size]; } // 析构函数 ~SafeArray() { delete[] ptr; } }; 

3.2 拷贝构造函数的注意事项

  1. 参数必须是引用:否则会导致无限递归调用

    // 错误!会导致无限递归 MyClass(MyClass other); 
  2. 处理自赋值情况:虽然拷贝构造中不常见,但应考虑

  3. 保持const正确性:源对象通常应为const引用

4. 拷贝构造函数与赋值运算符

4.1 区别与联系

特性 拷贝构造函数 赋值运算符
调用时机 创建新对象时 已存在对象赋值时
语法 ClassName(const ClassName&) ClassName& operator=(const ClassName&)
返回值 通常返回*this
默认行为 成员逐个拷贝 成员逐个赋值

4.2 三法则(Rule of Three)

如果一个类需要定义以下任何一个特殊成员函数,那么通常需要同时定义这三个:

  1. 析构函数
  2. 拷贝构造函数
  3. 拷贝赋值运算符
class RuleOfThree { public: // 1. 析构函数 ~RuleOfThree() { /* 释放资源 */ } // 2. 拷贝构造函数 RuleOfThree(const RuleOfThree& other) { /* 深拷贝实现 */ } // 3. 拷贝赋值运算符 RuleOfThree& operator=(const RuleOfThree& other) { if(this != &other) { /* 释放现有资源并深拷贝 */ } return *this; } }; 

5. 现代C++中的拷贝控制

5.1 五法则(Rule of Five)

C++11引入了移动语义,扩展为五法则:

  1. 析构函数
  2. 拷贝构造函数
  3. 拷贝赋值运算符
  4. 移动构造函数
  5. 移动赋值运算符
class RuleOfFive { public: // 1. 析构函数 ~RuleOfFive(); // 2. 拷贝构造函数 RuleOfFive(const RuleOfFive&); // 3. 拷贝赋值运算符 RuleOfFive& operator=(const RuleOfFive&); // 4. 移动构造函数 RuleOfFive(RuleOfFive&&) noexcept; // 5. 移动赋值运算符 RuleOfFive& operator=(RuleOfFive&&) noexcept; }; 

5.2 零法则(Rule of Zero)

理想情况下,类的资源管理应该委托给智能指针等RI对象,这样编译器生成的默认特殊成员函数就能正确工作:

class RuleOfZero { std::unique_ptr<Resource> resource; // 自动管理资源 std::vector<int> items; // 自动管理内存 // 不需要定义任何特殊成员函数 }; 

6. 拷贝构造函数的性能优化

6.1 避免不必要的拷贝

  1. 使用const引用传递参数

    void processObject(const MyClass& obj); // 避免拷贝 
  2. 返回值优化(RVO/NRVO):现代编译器会自动优化返回临时对象的场景

6.2 拷贝省略(Copy Elision)

编译器在某些情况下可以省略拷贝构造函数的调用:

MyClass create() { return MyClass(); // 可能直接构造在调用处,不调用拷贝构造函数 } 

7. 实际应用案例

7.1 字符串类的拷贝构造

class MyString { public: char* data; size_t length; // 拷贝构造函数 MyString(const MyString& other) : length(other.length) { data = new char[length + 1]; strcpy(data, other.data); } // 构造函数 MyString(const char* str = "") { length = strlen(str); data = new char[length + 1]; strcpy(data, str); } // 析构函数 ~MyString() { delete[] data; } }; 

7.2 禁止拷贝的类

某些类不应该允许拷贝操作(如线程类):

class NonCopyable { public: NonCopyable() = default; // 删除拷贝操作 NonCopyable(const NonCopyable&) = delete; NonCopyable& operator=(const NonCopyable&) = delete; }; 

8. 总结

  • 拷贝构造函数用于创建对象的副本
  • 默认实现是浅拷贝,对资源管理类需要自定义深拷贝
  • 遵循三法则/五法则确保资源安全
  • 现代C++提倡使用零法则简化代码
  • 理解拷贝构造有助于编写更高效、更安全的C++代码

掌握拷贝构造函数是成为C++高级开发者的重要一步,它直接关系到程序的正确性和性能。在实际开发中,应根据类的具体需求合理实现或禁用拷贝操作。 “`

这篇文章约2400字,涵盖了拷贝构造函数的核心概念、实现方法、相关规则和最佳实践,采用Markdown格式编写,包含代码示例和对比表格,便于理解和参考。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c++
AI