1. 程式人生 > >[基礎知識]3.拷貝建構函式

[基礎知識]3.拷貝建構函式

下列程式能夠執行成功嗎?如果不能,怎樣修改才能執行成功?

class A
{
private:
	int value;
	
public:
	A(int n) { value = n; }
	A(A other) { value = other.value; }
 
	void Print() { std::cout << value << std::endl; }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
 	A a = 2019;
 	A b = a;
	b.Print();
 
	return 0;
}

A. 編譯失敗
B. 編譯成功,執行時程式崩潰
C. 編譯執行正常,輸出2019

  • 分析:
    由於程式碼中拷貝建構函式A(A other)傳入的引數是A的一個例項(傳值呼叫),把形參複製到實參時又會呼叫拷貝建構函式,這樣就會形成遞迴呼叫,進而導致棧溢位。C++的標準不允許拷貝建構函式有傳值引數,因此在VS和GCC中都將編譯錯誤。答案:A
  • 修改程式碼:
    要解決這個問題,可以把拷貝建構函式修改為A(const A& other),也就是把傳值引數改成常量引用。
class A
{
private:
	int value;
	
public:
	A(int n) { value = n; }
	// 自定義拷貝建構函式的格式:類名::類名(const 類名 &物件名)
A(const A &other) { value = other.value; } void Print() { std::cout << value << std::endl; } }; int _tmain(int argc, _TCHAR* argv[]) { A a = 2019; A b = a; b.Print(); return 0; }

相關知識點:

  1. main()是標準C++的程式入口點函式,預設字元編碼格式為ANSI;
    其函式簽名為:int main(); 以及 int main(int argc, char* argv[]);
  2. _tmain()是微軟作業系統(windows)提供的對unicode字符集和ANSI字符集進行自動轉換用的程式入口點函式;函式簽名為: int _tmain(int argc, TCHAR *argv[]);
    ● 當你程式當前的字符集為unicode時,int _tmain(int argc, TCHAR *argv[]); 會被翻譯成: int wmain(int argc, wchar_t *argv[]);
    ● 當你程式當前的字符集為ANSI時,int _tmain(int argc, TCHAR *argv[]); 會被翻譯成: int main(int argc, char *argv[]);
  3. 第一個引數argc表示在命令列中輸入的程式名和引數個數之和,第二個引數中argv[0]記錄程式名,後面的argv[i]記錄輸入的引數。
  4. 函式簽名用於識別不同的函式,它包含了一個函式的資訊(函式名、引數型別、引數個數、順序以及它所在的類和名稱空間),但不包含函式返回值(如果兩個函式僅僅是返回值不同,那麼系統將無法區分這兩個函式,編譯器會提示語法錯誤)。
  5. 呼叫拷貝建構函式的三種情況:
    5.1 用類的一個物件去初始化該類的另一個物件時
A a2(a1); // 用代入法呼叫拷貝建構函式,用物件a1初始化物件a2
A a3=a1;  // 用賦值法呼叫拷貝建構函式,用物件a1初始化物件a3

5.2 函式的形參是類的物件,在呼叫函式進行形參和實參結合時

void fun(A a1){ // 形參是類A的物件a1
	p.Print();
}
int main(){
	A a(2019);
	fun(a); // 呼叫函式fun時,實參a是類A的物件,將呼叫拷貝建構函式初始化形參a1
}

5.3 函式的返回值是類的物件,在函式呼叫完畢將返回值(物件)帶回函式呼叫處時,會將此物件複製給一個臨時物件並傳到該函式的呼叫處。

A fun(){ // 函式fun的返回值型別是A類型別
	A a1(2019); // 定義類的物件a1
	return a1; // 函式的返回值是A類的物件
}
int main(){
	A a; // 定義類的物件a
	a = fun(); // 函式執行完畢,返回呼叫者時,呼叫拷貝建構函式
	return 0;
}
  1. 如果產生了新的物件例項,呼叫的是拷貝建構函式;如果沒有,呼叫的是賦值運算子函式。
int main(){
	A a1(2019), a2;
	a2 = a1; // 呼叫預設賦值運算子函式,將物件a1的值賦給物件a2
	A a3(a1), a4 = a2; 
	// 用代入法呼叫拷貝建構函式,用物件a1初始化物件a3
	// 用賦值法呼叫拷貝建構函式,用物件a2初始化物件a4
}
  1. ● 在向函式傳遞物件時,是通過“傳值呼叫”傳遞給函式的,即單向傳遞,只由實參傳給形參,而不能由形參傳回來給實參。
    ● 使用物件指標作為函式引數可以實現“傳址呼叫”,即在函式呼叫時使實參物件和形參物件指標變數指向同一記憶體地址。
    ● 使用物件引用作為函式引數不僅具有物件指標的優點,而且更簡單、直接。
  2. 自定義拷貝建構函式的格式:
類名::類名 ( const 類名 &物件名 )
{
	// 拷貝建構函式的函式體
}

參考文章
_tmain()和main()有什麼區別?
C++中的函式簽名