C++ 复制构造函数



复制构造函数

复制构造函数是一个构造函数,它通过用之前创建的同一类的对象初始化来创建对象。复制构造函数用于:

  • 从另一个相同类型的对象初始化一个对象。
  • 复制一个对象以将其作为参数传递给函数。
  • 复制一个对象以从函数返回它。

如果在类中没有定义复制构造函数,编译器本身会定义一个。如果类具有指针变量并进行了一些动态内存分配,则必须有一个复制构造函数。

语法

复制构造函数最常见的形式如下所示:

classname (const classname &obj) {
   // body of constructor
}

这里,obj 是一个对正在用于初始化另一个对象的对象的引用。

复制构造函数示例

以下示例演示了复制构造函数的使用

#include <iostream>

using namespace std;

class Line {

   public:
      int getLength( void );
      Line( int len );             // simple constructor
      Line( const Line &obj);  // copy constructor
      ~Line();                     // destructor

   private:
      int *ptr;
};

// Member functions definitions including constructor
Line::Line(int len) {
   cout << "Normal constructor allocating ptr" << endl;
   
   // allocate memory for the pointer;
   ptr = new int;
   *ptr = len;
}

Line::Line(const Line &obj) {
   cout << "Copy constructor allocating ptr." << endl;
   ptr = new int;
   *ptr = *obj.ptr; // copy the value
}

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;
}

// Main function for the program
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 );             // simple constructor
      Line( const Line &obj);  // copy constructor
      ~Line();                     // destructor

   private:
      int *ptr;
};

// Member functions definitions including constructor
Line::Line(int len) {
   cout << "Normal constructor allocating ptr" << endl;
   
   // allocate memory for the pointer;
   ptr = new int;
   *ptr = len;
}

Line::Line(const Line &obj) {
   cout << "Copy constructor allocating ptr." << endl;
   ptr = new int;
   *ptr = *obj.ptr; // copy the value
}

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;
}

// Main function for the program
int main() {

   Line line1(10);

   Line line2 = line1; // This also calls copy constructor

   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:
  // Constructor
  MyClass(int v) : value(v) {}

  // Explicit Copy Constructor
  MyClass(const MyClass& other) : value(other.value) {
    cout << "Explicit Copy Constructor called" << endl;
  }

  void display() const { cout << "Value: " << value << endl; }
};

void processValue(MyClass obj) {
  // Implicit copy constructor will be called here
  obj.display();
}

int main() {
  MyClass obj1(10);     // Constructor called
  MyClass obj2 = obj1;  // Explicit copy constructor called
  obj1.display();
  obj2.display();

  processValue(obj1);  // Implicit copy constructor called
  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& operator=(const ClassName& other)),以确保正确管理内存**。**
  • 五法则
    • **移动构造函数**(ClassName(ClassName&& other))。
    • **移动赋值运算符**(ClassName& operator=(ClassName&& other))”。

这些特殊成员函数对于正确管理动态内存和其他资源(如文件处理或网络连接)在类中是必要的。

深拷贝与浅拷贝

在 C++ 中,深拷贝和浅拷贝是复制对象的两种不同方式,当类涉及动态内存管理时,它们非常重要。

1. 浅拷贝

当以这样一种方式复制对象时,原始对象和复制对象共享相同的资源,就会发生浅拷贝。这意味着复制构造函数或复制赋值运算符仅复制数据成员(如指针)的值,而不会分配新的内存或创建资源的独立副本。

示例

#include <iostream>
using namespace std;

class MyClass {
 private:
  int* data;  // Pointer to an integer

 public:
  // Constructor
  MyClass(int value) {
    data = new int(value);  // Allocate memory
  }

  // Shallow Copy Constructor
  MyClass(const MyClass& other) {
    data = other.data;  // Copy pointer only
  }

  // Destructor
  ~MyClass() {
    delete data;  // Free memory
  }

  // Display the value
  void showData() const { cout << "Data: " << *data << endl; }
};

int main() {
  MyClass obj1(42);     // Create an object
  MyClass obj2 = obj1;  // Use shallow copy constructor

  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;  // Pointer to an integer

 public:
  // Constructor: Dynamically allocate memory 
  // and initialize with value
  MyClass(int value) { data = new int(value); }

  // Deep Copy Constructor
  // Allocates new memory and copies the value
  MyClass(const MyClass& other) { data = new int(*other.data); }

  // Destructor to clean up memory
  ~MyClass() { delete data; }

  // Display the value
  void showData() const { cout << "Data: " << *data << endl; }
};

int main() {
  MyClass obj1(42);     // Create an object
  MyClass obj2 = obj1;  // Use deep copy constructor

  obj1.showData();  // Display data from obj1
  obj2.showData();  // Display data from obj2

  return 0;
}

当以上代码编译并执行时,会产生以下结果:

Data: 42
Data: 42
广告