文章目錄
- C++中重載構造函數的互相調用初始化成員變量的問題
-
- 不能直接互相調用的重載構造函數
- 使用一個private的函數初始化成員變量
- 使用帶有預設參數的構造函數
- 使用placement new運算符調用重載構造函數
- 使用C++11的委托構造函數(在初始化清單位置調用)
C++中重載構造函數的互相調用初始化成員變量的問題
寫一個類的時候,我們可能會希望有多重不同的初始化方式,此時就用到了重載構造函數。例如:
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
這樣我們可以有兩種方式來初始化一個TestConstructor對象。
兩個重載構造函數實作的代碼可以分别是:
TestConstructor::TestConstructor()
{
this->value = 10;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
}
不能直接互相調用的重載構造函數
對于上邊的這個無參構造函數,我們可以直接初始化value為10,但是如果參數很多,重載構造函數很多,那麼就不可能每個都寫一遍一模一樣的指派操作(其實也可以,就是有些醜,而且如果發生改動,則有可能會因為疏忽而出錯)。是以,我們希望能夠減少重複的指派操作,例如調用其他的重載構造函數,就像這樣,
TestConstructor::TestConstructor()
{
TestConstructor(10);
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
}
但是這樣可以嘛?我們試一下,為了友善檢視實際生成的對象,我們在構造函數裡輸出對象的指針位址。
#include <iostream>
using namespace std;
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
TestConstructor::TestConstructor()
{
TestConstructor(10);
cout << "constructor1:" << this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
cout << "constructor2:" << this << endl;
}
int main()
{
TestConstructor *t = new TestConstructor();
cout << t->value << endl;
cout << t << endl;
delete t;
return 0;
}
運作結果:
constructor2:0x7fff81eb3494
constructor1:0x5586774bce70
0x5586774bce70
可以看出,在構造函數中調用重載構造函數時,被調用的構造函數重新建立了一個新的對象,是以不能用這種方法。
使用一個private的函數初始化成員變量
在我之前的項目中,為了解決這個問題,我采用了另外寫一個private的函數的方法,把一些需要反複初始化的部分扔進了一個private的函數裡。例如,
class TestConstructor
{
private:
void initialize(int);
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
void TestConstructor::initialize(int value)
{
this->value = value;
}
TestConstructor::TestConstructor()
{
this->initialize(10);
cout << "constructor1:" << this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->initialize(value);
cout << "constructor2:" << this << endl;
}
這個當然是一種解決方案。不過,最近用cppcheck analysis的時候,總是提示我
Member variable "xxx" is not initialized in the constructor.
, 這促使我思考了一下是否有更好的方法解決該問題。
使用帶有預設參數的構造函數
在C++的函數聲明中可以設定預設參數,是以一種可行的方法就是用含預設參數的構造函數來代替重載構造函數。如:
class TestConstructor
{
public:
int value;
public:
TestConstructor(int value = 10);
};
TestConstructor::TestConstructor(int value)
{
this->value = value;
}
(NOTE:注意預設參數通常放在函數聲明上,并且隻能寫一次,即聲明寫了,定義部分就不能再寫)
使用placement new運算符調用重載構造函數
C++提供在預先配置設定好的記憶體上建立對象的方法,即placement new運算符。在C++中,構造函數的執行實際上分為兩個階段,分别是初始化階段和計算階段,其中初始化階段先于計算階段。在進入計算階段前,所有的成員變量都已經完成了初始化,是以記憶體也都已經按需配置設定好,此時我們可以在this指針指向的這塊記憶體上調用重載構造函數建立一個新的對象,覆寫掉原來的對象。
#include <iostream>
using namespace std;
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int value);
};
TestConstructor::TestConstructor()
{
new (this)TestConstructor(10);
cout << "constructor1:" << this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
cout << "constructor2:" << this << endl;
}
int main()
{
TestConstructor *t = new TestConstructor();
cout << t->value << endl;
cout << t << endl;
delete t;
return 0;
}
運作結果:
constructor2:0x55bece811e70
constructor1:0x55bece811e70
10
0x55bece811e70
使用C++11的委托構造函數(在初始化清單位置調用)
據我了解這是C++11中加入的功能,名為委托構造函數。其将重載構造函數放置在初始化清單的位置調用。
#include <iostream>
using namespace std;
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
TestConstructor::TestConstructor():TestConstructor(10)
{
cout <<"constructor1:"<< this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
cout <<"constructor2:"<<this << endl;
}
int main()
{
TestConstructor *t = new TestConstructor();
cout << t->value << endl;
cout << t << endl;
delete t;
return 0;
}
運作結果:
constructor2:0x5594d644de70
constructor1:0x5594d644de70
10
0x5594d644de70
##總結
對于重載構造函數的互相調用初始化成員變量的問題,我們可以
- 使用一個私有函數初始化成員變量
- 使用帶有預設參數的構造函數
- 使用placement new運算符調用重載構造函數
- 使用C++11的委托構造函數(在初始化清單位置調用)