天天看點

C/C++程式設計筆記:詳細講解丨複制構造函數

什麼是複制構造函數? 

複制構造函數是一個成員函數,它使用相同類的另一個對象初始化一個對象。複制構造函數具有以下正常函數原型: 

    ClassName(const ClassName&old_obj);

以下是複制構造函數的一個簡單示例:

#include<iostream>

using namespace std;

class Point

{

private:

int x, y;

public:

Point(int x1, int y1) { x = x1; y = y1; }

// Copy constructor

Point(const Point &p1) {x = p1.x; y = p1.y; }

int getX() { return x; }

int getY() { return y; }

};

int main()

{

Point p1(10, 15); // Normal constructor is called here

Point p2 = p1; // Copy constructor is called here

// Let us access values assigned by constructors

cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();

cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();

return 0;

}
           

輸出: 

p1.x = 10, p1.y = 15

p2.x = 10, p2.y = 15

複制構造函數何時 調用?

在C ++中,在以下情況下可以調用複制構造函數: 

1.當類的對象按值傳回時。 

2.當類的對象通過值作為參數傳遞(傳遞給函數)時。 

3.基于同一類的另一個對象構造一個對象時。 

4.編譯器生成臨時對象時。

但是,不能保證在所有這些情況下都将調用複制構造函數,因為C ++标準允許編譯器在某些情況下優化複制,一個例子是傳回值優化(有時稱為RVO)。 

當是一個需要使用者定義的副本構造函數嗎?

如果我們不定義自己的副本構造函數,則C ++編譯器會為每個類建立一個預設的副本構造函數,該類在對象之間進行成員級複制。編譯器建立的複制構造函數通常可以正常工作。僅當對象具有指針或檔案句柄,網絡連接配接等資源的任何運作時配置設定時,才需要定義我們自己的副本構造函數。

預設構造函數僅執行淺表複制。

C/C++程式設計筆記:詳細講解丨複制構造函數

隻有使用者定義的副本構造函數才可以進行深層複制。在使用者定義的副本構造函數中,我們確定所複制對象的指針(或引用)指向新的記憶體位置。  

C/C++程式設計筆記:詳細講解丨複制構造函數

複制構造函數與指派運算符

以下兩個語句中的哪一個調用複制構造函數,而哪一個調用指派運算符? 

MyClass t1, t2;

MyClass t3 = t1; // ----> (1)

t2 = t1; // -----> (2)

從現有對象建立新對象作為現有對象的副本時,将調用複制構造函數。當已初始化的對象從另一個現有對象中配置設定了新值時,将調用指派運算符。在上面的示例中,(1)調用複制構造函數,(2)調用指派運算符。

寫一個需要複制構造函數的示例類嗎? 

以下是一個完整的C ++程式,以示範Copy構造函數的用法。在下面的String類中,我們必須編寫副本構造函數。 

#include<iostream>

#include<cstring>

using namespace std;

class String

{

private:

char *s;

int size;

public:

String(const char *str = NULL); // constructor

~String() { delete [] s; }// destructor

String(const String&); // copy constructor

void print() { cout << s << endl; } // Function to print string

void change(const char *); // Function to change

};

String::String(const char *str)

{

size = strlen(str);

s = new char[size+1];

strcpy(s, str);

}

void String::change(const char *str)

{

delete [] s;

size = strlen(str);

s = new char[size+1];

strcpy(s, str);

}

String::String(const String& old_str)

{

size = old_str.size;

s = new char[size+1];

strcpy(s, old_str.s);

}

int main()

{

String str1("GeeksQuiz");

String str2 = str1;

str1.print(); // what is printed ?

str2.print();

str2.change("GeeksforGeeks");

str1.print(); // what is printed now ?

str2.print();

return 0;

}
           

輸出: 

GeeksQuiz

GeeksQuiz

GeeksQuiz

GeeksforGeeks

如果我們從上述代碼中删除複制構造函數,那将會是什麼問題?

如果從上面的程式中删除copy構造函數,則不會獲得預期的輸出。對str2所做的更改也反映在str1中,這是意料之外的。 

#include<iostream>

#include<cstring>

using namespace std;

class String

{

private:

char *s;

int size;

public:

String(const char *str = NULL); // constructor

~String() { delete [] s; }// destructor

void print() { cout << s << endl; }

void change(const char *); // Function to change

};

String::String(const char *str)

{

size = strlen(str);

s = new char[size+1];

strcpy(s, str);

}

void String::change(const char *str)

{

delete [] s;

size = strlen(str);

s = new char[size+1];

strcpy(s, str);

}

int main()

{

String str1("GeeksQuiz");

String str2 = str1;

str1.print(); // what is printed ?

str2.print();

str2.change("GeeksforGeeks");

str1.print(); // what is printed now ?

str2.print();

return 0;

}
           

輸出: 

GeeksQuiz

GeeksQuiz

GeeksforGeeks

GeeksforGeeks

我們可以将複制構造函數設為私有嗎?

是的,可以将複制構造函數設為私有。當我們在一個類中将複制構造函數設為私有時,該類的對象将變為不可複制。當我們的類具有指針或動态配置設定的資源時,這特别有用。在這種情況下,我們可以像上面的String示例一樣編寫我們自己的副本構造函數,也可以建立一個私有副本構造函數,以便使用者獲得編譯器錯誤,而不是在運作時感到意外。 

為什麼必須将複制構造函數的參數作為引用傳遞?

按值傳遞對象時,将調用複制構造函數。複制構造函數本身就是一個函數。是以,如果我們在複制構造函數中按值傳遞參數,則将調用複制構造函數來調用複制構造函數,這将成為一個無終止的調用鍊。是以,編譯器不允許參數按值傳遞。

以上就是今天的全部内容了。每日分享小知識,希望對你有幫助~

另外如果你想更好的提升你的程式設計能力,學好C語言C++程式設計!彎道超車,快人一步!筆者這裡或許可以幫到你~

C語言C++程式設計學習交流圈子,QQ群:765803539【點選進入】微信公衆号:C語言程式設計學習基地

分享(源碼、項目實戰視訊、項目筆記,基礎入門教程)

歡迎轉行和學習程式設計的夥伴,利用更多的資料學習成長比自己琢磨更快哦!

程式設計學習視訊分享:

C/C++程式設計筆記:詳細講解丨複制構造函數

繼續閱讀