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
廣告