C++拷貝構造函數詳解

C++拷貝構造函數詳解

簡介

拷貝構造函數是C++獨有的一種特殊的構造函數,以同型對象初始化自我對象。ios

拷貝構造函數是一種特殊的構造函數,具備單個形參,該形參(經常使用const修飾)是對該類類型的引用。當定義一個新對象並用一個同類型的對象對它進行初始化時,將顯示使用拷貝構造函數。當該類型的對象傳遞給函數或從函數返回該類型的對象時,將隱式調用拷貝構造函數。c++

拷貝構造函數

拷貝構造函數是一種特殊的構造函數,它在建立對象時,是使用同一類中以前建立的對象來初始化新建立的對象。拷貝構造函數一般用於:函數

  • 經過使用另外一個同類型的對象來初始化新建立的對象。
  • 複製對象把它做爲參數傳遞給函數。
  • 複製對象,並從函數返回這個對象。

若是用戶沒有定義拷貝構造函數,可是調用了拷貝構造函數,那麼編譯器會自動生成一個默認的拷貝構造函數。可是若是本身定義了拷貝構造函數,編譯器則不在生成。this

最多見的形式以下:spa

classname (const classname& obj){
    // code here
}

下面對三種調用拷貝構造函數的狀況進行一一說明:code

經過使用另外一個同類型的對象來初始化新建立的對象

#include <iostream>

using namespace std;

class Student {
public:
	Student();	// default構造函數
	Student(const Student& obj);	// 拷貝構造函數

	int getNumber();

private:
	int number;
};

// 定義默認構造函數
Student::Student(){
	this->number = 0;
	cout << "default constructor" << endl;
}

// 定義拷貝構造函數
Student::Student(const Student& obj) {
	this->number = obj.number;
	cout << "copy constructor" << endl;

}

int Student::getNumber() {
	return this->number;
}

int main(){

	Student s1;		  // 調用默認構造函數
	Student s2(s1);	  // 調用拷貝構造函數
	Student s3 = s2;  // 調用拷貝構造函數
	cout << s1.getNumber() << endl;
	cout << s2.getNumber() << endl;
	cout << s3.getNumber();

	return 0;
}


/*

Output

default constructor
copy constructor
copy constructor
0
0
0

*/

這裏建立了三個對象,s1 s2 s3。對象

s1是調用了默認的構造函數,number爲0。字符串

s2是調用了拷貝構造函數,傳入了s1,複製了s1的number,輸出了"copy constructor"。get

s3的建立是一種特殊的調用拷貝構造函數的方法。注意,若是有一個新對象被建立,那麼必定會調用構造函數。這裏就是傳入了s2到咱們定義的拷貝構造函數裏。編譯器

可見,經過使用另外一個同類型的對象來初始化新建立的對象,拷貝構造函數的用處是很明顯的。

複製對象把它做爲參數傳遞給函數

拷貝構造函數是一個很是重要的函數,其之因此重要,是由於它定義了一個對象如何以值傳遞給函數

試想以下代碼:

#include <iostream>

using namespace std;

class Student {
public:
	Student();	// default構造函數
	Student(const Student& obj);	// 拷貝構造函數

	int getNumber();

private:
	int number;
};

// 定義默認構造函數
Student::Student(){
	this->number = 0;
    cout << "default constructor" << endl;
}

// 定義拷貝構造函數
Student::Student(const Student& obj) {
	this->number = obj.number;
    cout << "copy constructor" << endl;
}

int Student::getNumber() {
	return this->number;
}

// 輸出某個對象的number
void showNumber(Student a) {
	cout << a.getNumber();
}

int main(){
	Student s;  // 調用默認構造函數
	showNumber(s);

	return 0;
}

你認爲輸出結果會是什麼?首先確定會輸出一個"default constructor",由於s的默認構造函數輸出了該字符串。那麼,showNumber()會輸出什麼呢?正確的輸出是:

default constructor
copy constructor
0

這就回到了咱們開頭的那句話,拷貝構造函數定義了一個對象如何以值傳遞給函數

在函數showNumber()中,參數a是以值傳遞的方式傳入的。因此主函數中調用showNumber()時,對象s被複制到了形參a當中。這個複製操做是經過調用了拷貝構造函數完成的,因此調用了自身的拷貝構造函數,天然會輸出一遍"copy constructor"。

至關於執行了這樣一個操做

Student a(s);

等待showNumber()結束以後,再析構a這個對象。

這也就是爲何傳遞用戶自定類型一般用引用傳遞,同時也解釋了,爲何拷貝構造函數傳參爲何是引用傳參?

設想,若是拷貝構造函數這麼寫,會發生什麼?

Student (const Student obj);

假設有個Student對象s。我值傳遞s進入了拷貝構造函數,那麼按照以前所說,編譯器會去申請一個空間給形參obj,同時調用自身的拷貝構造函數把s的數據成員的值傳給obj。可是這個調用拷貝構造函數的過程,又是一個值傳遞,又須要開闢一個空間....

因此,只要以值傳遞的方式調用拷貝構造函數,就會申請空間,申請空間的過程當中又會申請空間,又申請空間的過程又又申請空間。如此往復,會陷入無限套娃的死循環。

因此拷貝構造函數必定不能是值傳遞。

複製對象,並從函數返回這個對象

返回類型爲類對象的函數,若是返回類型不是引用,在執行return時,會建立一個臨時對象,並調用拷貝構造函數給這個臨時對象賦值。

#include <iostream>

using namespace std;

class Student {
public:
	Student();	// default構造函數
	Student(const Student& obj);	// 拷貝構造函數

	int getNumber();

private:
	int number;
};

// 定義默認構造函數
Student::Student(){
	this->number = 0;
    cout << "default constructor" << endl;
}

// 定義拷貝構造函數
Student::Student(const Student& obj) {
	this->number = obj.number;
    cout << "copy constructor" << endl;
}

int Student::getNumber() {
	return this->number;
}

Student returnS() {
	Student s;
	return s;
}

int main(){
	cout << returnS().getNumber();
	return 0;
}

// output
/*

default constructor
copy constructor
0

*/

咱們在函數returnS()中,建立了局部對象s。s在建立的時候,調用了默認的構造函數,輸出"default constructor"。然而在return的時候,調用了拷貝構造函數,建立了一個看不見的對象,因此再次輸出"copy constructor"。

因此,咱們能夠經過返回類對象的引用,這樣就會返回現存對象,而不是臨時建立的對象。(固然,局部變量是不能夠引用傳回的,臨時變量在函數結束時銷燬,引用爲空)

相關文章
相關標籤/搜索