總結
- 初始化值的是創建變量時賦予變量一個值(不同於賦值的概念)
- 使用等號
=
初始化對象時是拷貝初始化,否則是直接初始化 - 直接初始化也可能調用拷貝構造函數,拷貝初始化可以不調用拷貝構造函數
初始化概念
對象是類的實例化,在內存中會佔據一個一定大小的空間。創建一個對象分爲兩步:分配內存空間和初始化,剛剛分配的空間有可能包含髒數據,因此我們需要通過初始化函數(C++中指的是構造函數)對分配的空間進行正確地初始化以保證對象值的合法性。
初始化指的就是創建變量是賦予它一個值(不同於賦值的概念),類的構造函數控制其對象的初始化過程,無論何時只要類的對象被創建就會執行構造函數。
拷貝初始化與直接初始化
C++由於歷史原因包含多種不同的初始化方式,我們可以簡單地認爲:如果使用等號=
初始化變量則執行的是拷貝初始化(編譯器將等號右邊的對象值拷貝到新創建的對象中去),不使用等號時使用的是直接初始化。
string s1 = "tomocat"; // 拷貝初始化
string s2("tomocat"); // 直接初始化
string s3(10, 'c'); // 直接初始化, s3內容爲cccccccccc
// s4拷貝初始化
string s4 = string(10, 'c');
// 等價於
string temp = string(10, 'c');
string s4 = temp;
注意當我們使用直接初始化時(不用等號=
初始化變量),實際上是要求編譯器使用普通的函數匹配來選擇與我們提供的參數最匹配的構造函數(當然也包括拷貝構造函數),當我們使用拷貝初始化時(使用=
初始化變量),我們要求編譯器將右側運算對象的值拷貝到正在創建的對象中,如果有需要的話還需要進行類型轉換。
實戰
儘管直接初始化和拷貝初始化的定義如上所示,但是由於直接初始化可能調用拷貝構造函數,拷貝初始化不一定調用拷貝構造函數,我們還是結合一些實例來看一下。我們定義如下的結構體:
struct Cat {
// 默認構造函數
Cat() {
printf("Cat()\n");
}
// 析構函數
~Cat() {
printf("~Cat()\n");
}
// 構造函數
Cat(const Cat &cat) {
printf("Cat(const Cat &cat)\n");
}
// 接受string對象的構造函數
Cat(std::string n) : name(n) {
printf("Cat(std::string n)\n");
}
// 拷貝賦值運算符
Cat& operator=(const Cat &cat) {
printf("Cat& operator=(const Cat &cat)\n");
return *this;
}
std::string name;
};
幾種常用的初始化實現方式如下:
int main(void) {
// 拷貝初始化: 執行std::string到Cat的隱式類型轉換
Cat cat1 = std::string("tomo");
printf("-------------------------------------------------------------------\n");
// 拷貝初始化: 調用拷貝構造函數而不是拷貝賦值運算符(因爲這裏是初始化而不是賦值)
Cat cat2 = cat1;
printf("-------------------------------------------------------------------\n");
// 直接初始化: 調用拷貝構造函數
Cat cat3(cat2);
printf("-------------------------------------------------------------------\n");
return 0;
}
輸出信息如下:
Cat(std::string n)
-------------------------------------------------------------------
Cat(const Cat &cat)
-------------------------------------------------------------------
Cat(const Cat &cat)
-------------------------------------------------------------------
~Cat()
~Cat()
~Cat()
Reference
[1] https://blog.csdn.net/sirenxiaohuayuan/article/details/90142419