C++中类的复制构造函数

1 简介

1.1 概念

复制构造函数(copy constructor)是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。

1.2 格式

假设类的名称是Foo,则Foo类的复制构造函数的声明为

class Foo{

  public:

      Foo();             //默认构造函数

      Foo(const Foo&); //复制构造函数

//....

}

1.3 默认的复制构造函数

如果类中没有定义复制构造函数,则编译器会生成一个默认的复制构造函数。默认的复制构造函数将类的成员进行逐个初始化。

2 应用

假设Foo类的如1.2中格式声明,有如下代码

<think>我们正在讨论C++中的复制构造函数(Copy Constructor)。复制构造函数是一种特殊的构造函数,用于创建一个新对象作为现有对象的副本。它在以下情况下被调用: 1. 通过另一个同型的对象显式或隐式初始化一个对象。 2. 对象作为函数参数按值传递时。 3. 函数返回一个对象时(按值返回)。 复制构造函数的声明形式为:`ClassName(const ClassName &other);` 如果没有显式定义复制构造函数,编译器会自动生成一个默认的复制构造函数,该函数执行浅拷贝(逐个成员复制)。如果中有指针成员或需要管理资源(如动态内存),则必须自定义复制构造函数以实现深拷贝,避免浅拷贝导致的问题(如双重释放)。 下面我们通过一个示例来详细说明: ```cpp #include <iostream> #include <cstring> // for strcpy class String { private: char* data; int length; public: // 普通构造函数 String(const char* str) { length = strlen(str); data = new char[length + 1]; strcpy(data, str); } // 复制构造函数(深拷贝) String(const String& other) { length = other.length; data = new char[length + 1]; strcpy(data, other.data); } // 析构函数 ~String() { delete[] data; } // 打印字符串 void print() const { std::cout << data << std::endl; } }; int main() { String str1("Hello"); String str2 = str1; // 调用复制构造函数 str1.print(); // 输出: Hello str2.print(); // 输出: Hello return 0; } ``` 在上面的代码中: - 我们定义了一个`String`,它有一个指针成员`data`指向动态分配的字符数组。 - 我们定义了复制构造函数,它通过分配新的内存并复制内容来实现深拷贝。这样,每个对象都有自己的数据副本。 - 如果没有自定义复制构造函数,那么默认的复制构造函数会直接复制指针(即两个对象指向同一块内存),当两个对象析构时,同一块内存会被释放两次,导致未定义行为(通常是程序崩溃)。 注意:复制构造函数的参数必须是同型对象的常量引用(`const String&`),原因如下: 1. 使用引用避免无限递归(如果按值传递,会调用复制构造函数,从而再次按值传递,导致无限递归)。 2. 使用`const`因为复制构造函数不应该修改源对象。 ### 何时需要自定义复制构造函数? 当满足以下任一条件时,通常需要自定义复制构造函数: - 有指针成员并指向动态分配的内存。 - 有成员表示不可共享的资源(如文件句柄、网络连接等)。 ### 禁止复制 如果不想让对象被复制,可以将复制构造函数声明为私有并不提供实现(C++11以后可以用`=delete`)。 ```cpp class NonCopyable { private: NonCopyable(const NonCopyable&); // 只声明不实现 public: NonCopyable() {} }; // 或者使用C++11的delete class NonCopyable { public: NonCopyable() {} NonCopyable(const NonCopyable&) = delete; }; ``` §§相关问题§§: 1. 什么是浅拷贝和深拷贝?它们有什么区别? 2. 在C++中,什么时候会调用复制构造函数? 3. 如何防止一个复制? 4. C++11中的移动构造函数是什么?它与复制构造函数有何不同?</think># C++ 复制构造函数详解 ## 复制构造函数概念 复制构造函数是一种特殊的构造函数,用于创建一个新对象作为现有对象的副本。它在以下情况下被调用: 1. 使用一个对象初始化另一个对象时 2. 对象作为函数参数按值传递时 3. 函数返回对象时(按值返回) ## 基本语法 ```cpp class MyClass { public: // 复制构造函数声明 MyClass(const MyClass& other); }; ``` ## 完整示例代码 ```cpp #include <iostream> #include <cstring> // 用于字符串操作 class Person { private: char* name; int age; public: // 普通构造函数 Person(const char* n, int a) : age(a) { name = new char[strlen(n) + 1]; strcpy(name, n); std::cout << "普通构造函数: " << name << std::endl; } // 复制构造函数(深拷贝实现) Person(const Person& other) : age(other.age) { name = new char[strlen(other.name) + 1]; strcpy(name, other.name); std::cout << "复制构造函数: " << name << std::endl; } // 析构函数 ~Person() { delete[] name; std::cout << "析构函数: " << name << std::endl; } // 显示信息 void display() const { std::cout << "姓名: " << name << ", 年龄: " << age << std::endl; } }; // 函数演示按值传递对象 void displayPerson(Person p) { std::cout << "函数内: "; p.display(); } int main() { // 情况1: 使用一个对象初始化另一个对象 Person person1("张三", 25); Person person2 = person1; // 调用复制构造函数 std::cout << "\n对象信息:\n"; person1.display(); person2.display(); // 情况2: 按值传递对象 std::cout << "\n按值传递演示:\n"; displayPerson(person1); // 情况3: 函数返回对象 std::cout << "\n函数返回对象演示:\n"; Person createPerson() { Person temp("李四", 30); return temp; // 可能调用复制构造函数(取决于编译器优化) } Person person3 = createPerson(); person3.display(); return 0; } ``` ## 关键概念解释 ### 1. 浅拷贝 vs 深拷贝 - **浅拷贝**:默认复制构造函数执行浅拷贝(简单复制指针值) - **深拷贝**:需要自定义复制构造函数复制指针指向的内容(如上例中的`name`) ### 2. 必须自定义复制构造函数的情况 当包含以下成员时,必须自定义复制构造函数: - 原始指针(管理动态内存) - 文件句柄 - 网络连接 - 其他不可共享的资源 ### 3. 复制构造函数参数 - **必须使用常量引用**:`const MyClass& other` - 使用值传递会导致无限递归(创建临时对象时又调用复制构造函数) ### 4. 编译器行为 - 如果未定义复制构造函数,编译器会自动生成一个(执行浅拷贝) - 当有指针成员时,默认生成的复制构造函数通常不安全 ## 输出结果说明 运行上述代码将显示: ``` 普通构造函数: 张三 复制构造函数: 张三 对象信息: 姓名: 张三, 年龄: 25 姓名: 张三, 年龄: 25 按值传递演示: 复制构造函数: 张三 函数内: 姓名: 张三, 年龄: 25 析构函数: 张三 函数返回对象演示: 普通构造函数: 李四 复制构造函数: 李四 // 可能被编译器优化掉 析构函数: 李四 姓名: 李四, 年龄: 30 析构函数: 李四 析构函数: 张三 析构函数: 张三 ``` ## 最佳实践 1. **三法则**:如果需要自定义析构函数,通常也需要自定义复制构造函数复制赋值操作符 2. **使用`const`引用**:确保复制构造函数不修改源对象 3. **资源管理**:对动态分配的资源进行深拷贝 4. **考虑禁用复制**:对于不可复制的对象,使用`= delete`禁用复制构造函数 ## 禁用复制构造函数 对于不可复制(如管理独占资源的): ```cpp class NonCopyable { public: NonCopyable() = default; // 禁用复制构造函数 NonCopyable(const NonCopyable&) = delete; // 禁用复制赋值操作符 NonCopyable& operator=(const NonCopyable&) = delete; }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值