[C++] - 中的複製初始化(copy initialization)

過去在學習C++中類對象創建時,對constructor關注的很多,天真地認爲了解了constructor,copy constructor,copy assignment就對類對象的創建理解了,今天遇到的一個看似不起眼的問題讓我認識了還有copy initialization這麼個東東,通過網上查閱揭開了copy initialization寫神祕面紗,下面講一下自己的一點理解。

首先看一下問題代碼:

 

class String{
public:
	String() :str(""){ cout << "default constructor" << endl; }
	String(int n);

	String(const String &x) :str(x.str)
	{
		cout << "copy constructor" << endl;
	}
	String& operator=(const String &x)
	{
		str = x.str;
		cout << "operator =" << endl;
		return *this;
	}
	string getStr() const
	{
		return str;
	}
private:
	string str;
};

ostream& operator<<(ostream &o,const String &x)
{
	o << x.getStr();
	return o;
}

String::String(int n)
{
	stringstream ss;
	ss << n;
	ss >> str;
	ss.clear();
	ss.str("");
	cout << "String(int n)" << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	String s6 = 10;
	cout << s6 << endl;
	return 0;
}


運行結果如下圖所示:

 

首先對於等式右邊的10,能夠理解使用了隱式轉換調用了String(int n)創建了一個String對象,運行結果也確實是這樣。但是爲什麼之後沒有調用copy assignment等號操作符重載?原來關鍵是使用了copy initialization

其實,String s6 = 10;這不是一個賦值,這是一個初始化,準確的說是複製初始化(copy initialization)。

copy initializaiton: initializes an object from another object

copy initialization有以下幾種形式:

 

T object = other; (1)  
 
T object = {other; (2) (until C++11)
 
f(other) (3)  
 
return other; (4)  
 
throw object;

catch (T object)

(5)  
 
T array[N] = {other}; (6)

本文中的是第一種形式。它的實現過程準確的說是:

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution. The result of the conversion, which is a prvalue temporary(until C++17)prvalue expression (since C++17) if a converting constructor was used, is then used to direct-initialize the object. The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used. (until C++17)

也就是說,當other不是T類或T類子類的對象時,將會選擇一個最佳的轉換函數將other轉換成T類。在本文中String(int n)就是上文中的converting constructor,而converting constructor是屬於user-defined conversion sequences的一部分。因此本文中String(int n)將會被調用將10轉變爲String對象,轉換的結果是一個prvalue temporary(類似右值的臨時值),這個prvalue temporary然後將被用來直接初始化(direct initialize)T類對象(在本文中就是s6)。但是直接初始化(direct initialize)這步通常被優化了,即轉換的結果直接在內存中construct並且分配給目標對象。在本文中,就是在棧上調用String(10)創建了一個String類的對象,然後將這個對象直接給s6,過程等價於執行了s6(10),所以纔會出現運行結果中的只執行了String(int n),並沒有執行copy assignment,也就是上文所說的even though it's not used

 

自己的簡單理解,如有錯誤,歡迎指正

 

參考鏈接:

copy initialization: http://en.cppreference.com/w/cpp/language/copy_initialization

converting constructor:http://en.cppreference.com/w/cpp/language/converting_constructor

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章