C++ 复制构造函数
复制构造函数
复制构造函数是一种构造函数,它通过使用先前创建的同一类的对象来初始化一个对象来创建一个对象。复制构造函数用于:-
- 用同类型的另一个对象初始化一个对象。
- 复制一个对象并将其作为参数传递给函数。
- 复制一个对象并将其从函数中返回。
如果类中未定义复制构造函数,则编译器会自行定义一个。如果类包含指针变量并进行动态内存分配,则必须拥有复制构造函数。
语法
复制构造函数的最常见形式如下所示:-
classname (const classname &obj) { // 构造函数主体 }
此处,obj 是对用于初始化另一个对象的对象的引用对象。
复制构造函数示例
以下示例演示了复制构造函数的用法:
#include <iostream> using namespace std; class Line { public: int getLength( void ); Line( int len ); // 简单构造函数 Line( const Line &obj); // 复制构造函数 ~Line(); // 析构函数 private: int *ptr; }; // 成员函数定义包括构造函数 Line::Line(int len) { cout << "Normal constructor allocating ptr" << endl; // 为指针分配内存; ptr = new int; *ptr = len; } Line::Line(const Line &obj) { cout << "Copy constructor allocating ptr." << endl; ptr = new int; *ptr = *obj.ptr; //复制值 } Line::~Line(void) { cout << "Freeing memory!" << endl; delete ptr; } int Line::getLength( void ) { return *ptr; } void display(Line obj) { cout << "Length of line : " << obj.getLength() <<endl; } // 程序的主函数 int main() { Line line(10); display(line); return 0; }
当编译并执行上述代码时,它会产生以下结果 -
Normal constructor allocating ptr Copy constructor allocating ptr. Length of line : 10 Freeing memory! Freeing memory!
使用复制构造函数创建新对象
您可以使用复制构造函数的概念,利用现有对象创建新对象。
在下面的示例中,复制构造函数用于创建一个新对象作为现有对象的副本。
示例
让我们看一个相同的示例,但略作修改,使用相同类型的现有对象创建另一个对象 -
#include <iostream> using namespace std; class Line { public: int getLength( void ); Line( int len ); // 简单构造函数 Line( const Line &obj); // 复制构造函数 ~Line(); // 析构函数 private: int *ptr; }; // 成员函数定义包括构造函数 Line::Line(int len) { cout << "Normal constructor allocating ptr" << endl; // 为指针分配内存; ptr = new int; *ptr = len; } Line::Line(const Line &obj) { cout << "Copy constructor allocating ptr." << endl; ptr = new int; *ptr = *obj.ptr; // 复制值 } Line::~Line(void) { cout << "Freeing memory!" << endl; delete ptr; } int Line::getLength( void ) { return *ptr; } void display(Line obj) { cout << "Length of line : " << obj.getLength() <<endl; } // 程序的主函数 int main() { Line line1(10); Line line2 = line1; // 这也调用复制构造函数 display(line1); display(line2); return 0; }
当编译并执行上述代码时,它会产生以下结果 -
Normal constructor allocating ptr Copy constructor allocating ptr. Copy constructor allocating ptr. Length of line : 10 Freeing memory! Copy constructor allocating ptr. Length of line : 10 Freeing memory! Freeing memory! Freeing memory!
隐式与显式复制构造函数
在 C++ 中,有两种类型的复制构造函数:隐式和显式。本文我们将讨论这两者之间的区别。
隐式复制构造函数
如果用户未定义自己的复制构造函数,则编译器会自动提供一个隐式复制构造函数。它执行对象的浅拷贝,这意味着它将对象每个成员的值复制到新对象中。
何时调用隐式复制构造函数?
- 当用户将对象按值传递给函数时。
- 当用户从函数按值返回对象时。
- 当用户使用同类型的另一个对象初始化一个对象时(复制初始化)。
显式(用户定义)复制构造函数
它是用户定义的构造函数。这使您可以自定义复制行为,例如创建深层复制而不是默认的浅层复制。
示例
以下是 C++ 中显式和隐式复制构造函数的示例:
#include <iostream> using namespace std; class MyClass { private: int value; public: // 构造函数 MyClass(int v) : value(v) {} // 显式复制构造函数 MyClass(const MyClass& other) : value(other.value) { cout << "Explicit Copy Constructor called" << endl; } void display() const { cout << "Value: " << value << endl; } }; void processValue(MyClass obj) { // 此处将调用隐式复制构造函数 obj.display(); } int main() { MyClass obj1(10); // 调用构造函数 MyClass obj2 = obj1; // 调用显式复制构造函数 obj1.display(); obj2.display(); processValue(obj1); // 调用隐式复制构造函数 return 0; }
当编译并执行上述代码时,它会产生以下结果 -
Explicit Copy Constructor called Value: 10 Value: 10 Explicit Copy Constructor called Value: 10
三/五规则
三规则和五规则建议,在定义复制构造函数 (ClassName(const ClassName& other)) 时,还应定义:
三规则和五规则建议,在定义复制构造函数 (ClassName(const ClassName& other)) 时,还应定义:
- 三规则:
- 析构函数(~ClassName())。
- 以及复制赋值运算符(ClassName& operant=(const ClassName& other)),以确保正确管理内存。
- 五规则:
- 移动构造函数(ClassName(ClassName&& other)).
- 移动赋值运算符 (ClassName& 运算符=(ClassName&& other))".
这些特殊的成员函数对于正确管理类中的动态内存和其他资源(例如文件处理或网络连接)至关重要。
深拷贝 vs. 浅拷贝
在 C++ 中,深拷贝和浅拷贝是复制对象的不同方式,当类涉及动态内存管理时,它们非常重要。
1. 浅拷贝
当对象以某种方式复制时,原始对象和复制对象共享相同的资源,就会发生浅拷贝。这意味着复制构造函数或复制赋值运算符仅复制数据成员(如指针)的值,而不分配新内存或创建资源的独立副本。
示例
#include <iostream> using namespace std; class MyClass { private: int* data; // 指向整数的指针 public: // 构造函数 MyClass(int value) { data = new int(value); // 分配内存 } // 浅拷贝构造函数 MyClass(const MyClass& other) { data = other.data; // 仅复制指针 } // 析构函数 ~MyClass() { delete data; // 释放内存 } // 显示值 void showData() const { cout << "Data: " << *data << endl; } }; int main() { MyClass obj1(42); // 创建对象 MyClass obj2 = obj1; // 使用浅拷贝构造函数 obj1.showData(); obj2.showData(); return 0; }
当编译并执行上述代码时,它会产生以下结果 -
Data: 42 Data: 42 free(): double free detected in tcache 2
2. 深层复制
对象复制时,会为其自身分配新的内存,以确保原始对象和复制对象完全独立。避免双重释放错误或悬空指针。
示例
#include <iostream> using namespace std; class MyClass { private: int* data; // 指向整数的指针 public: // 构造函数:动态分配内存 // 并使用值进行初始化 MyClass(int value) { data = new int(value); } // 深拷贝构造函数 // 分配新内存并复制值 MyClass(const MyClass& other) { data = new int(*other.data); } // 析构函数用于清理内存 ~MyClass() { delete data; } // 显示值 void showData() const { cout << "Data: " << *data << endl; } }; int main() { MyClass obj1(42); // 创建对象 MyClass obj2 = obj1; // 使用深拷贝构造函数 obj1.showData(); // 显示 obj1 中的数据 obj2.showData(); // 显示 obj2 中的数据 return 0; }
当编译并执行上述代码时,它会产生以下结果 -
Data: 42 Data: 42