讀者若是以爲我文章還不錯的,但願能夠多多支持下我,文章能夠轉發,可是必須保留原出處和原做者署名。更多內容請關注個人微信公衆號:cpp手藝人linux
//oa的一系列操做...
OptimizationA GetOpt() {
OptimizationA oa;
//oa的一系列操做...
return oa;
}
void GetOpt(OptimizationA &_result) {
// result的一系列操做...
return;
}
複製代碼
思考:效率是同樣的?若是是不同的,那麼又是如何不同的?那咱們如何作效率更好呢?c++
咱們本身寫的代碼,本身看一回事,可是在編譯器的角度來看又是一番風景。因此此次咱們換個角度來看待問題,分別從初始化操做、優化、成員列表初始化三個方面探究下編譯器會怎麼翻譯咱們的代碼。程序員
OptimizationA oe;
OptimizationA of(oe);
OptimizationA og = oe;
OptimizationA oh = OptimizationA(oe);
// 編譯器的角度看,分紅兩步走,
// 第一步:定義變量(不會調用初始化操做),第二步:調用拷貝構造
// 1.OptimizationA of (注意此時不會調用OptimizationA的默認構造函數)
// 2.of.OptimizationA::OptimizationA(oe) (調用拷貝構造函數)
// 3.og.OptimizationA::OptimizationA(oe) (調用拷貝構造函數)
// 4.oh.OptimizationA::OptimizationA(oe) (調用拷貝構造函數)
複製代碼
void Parameter(OptimizationA oa) {
}
{
OptimizationA tempoa;
Parameter(tempoa);
}
// 編譯器生成的代碼
OptimizationA _tempObj<font>;
// tempObj調用copy構造
tempObj.OptimizationA::OptimizationA(tempoa);
Parameter(tempObj);
// tempObj調用析構函數,銷燬對象
tempObj.OptimizationA::~OptimizationA();
複製代碼
OptimizationA GetOpt() {
OptimizationA oa;
return oa;
}
// 此爲編譯器的生成的函數,分爲兩步操做
// 第一步:將上面的函數重寫爲下面的帶引用參數的形式
void GetOpt(OptimizationA &_result) {
OptimizationA oa;
//oa的一系列操做。。。。。。
// 第二步:在return返回以前,調用result的copy 構造函數
result::OptimizationA::OptimizationA(oa);
return;
}
// 下面是編譯器生成的調用代碼
// 1.形式轉換成這樣
OptimizationA result;
GetOpt(result);
// 2.若是用戶調用了類成員函數
GetOpt().GetHello();
// 編譯器則轉換成這樣
(GetOpt(result), result).GetHello();
// 3.若是是用戶定義了函數指針
OptimizationA (*pf)();
pf = GetOpt; // 沒有參數
// 編譯器則轉換成這樣
void (*pf)(OptimizationA &);
(pf(result), result).GetHello();
複製代碼
// 程序員的未優化
OptimizationA GetOpt(const T &y, const T &x) {
OptimizationA oa(x, y);
// oa其餘操做
return oa;
}
// 在linux上測試須要關閉優化選項
// 先是生成了一個臨時對象tempobj,而後調用tempobj的拷貝構造函數,將oa的數據拷貝到
// tempobj中,而後在調用oa的析構函數。
// 這個過程當中消耗了一個tempobj的拷貝構造和析構函數
// 程序員優化,這樣作就少了一個臨時對象的生成和銷燬
OptimizationA GetOpt(const T &x, const T &y) {
return OptimizationA(x, y);
}
複製代碼
未優化代碼 | 優化代碼 |
---|---|
Linux上關閉優化選項結果: compiler:1 level:2 call ctor compiler:2 level:3 call copy ctor compiler:1 level:2 call dtor compiler:3 level:4 call copy ctor compiler:2 level:3 call dtor compiler:3 level:4 call dtor Linux不關閉優化選項: compiler:1 level:2 call ctor compiler:1 level:2 call dtor windows上: compiler:1 level:2 call ctor compiler:2 level:3 call copy ctor compiler:1 level:2 call dtor compiler:2 level:3 call dtor |
Linux: compiler:1 level:2 call ctor compiler:1 level:2 call dtor 在windows上: compiler:1 level:2 call ctor compiler:1 level:2 call dtor |
// 程序員寫的代碼
OptimizationA GetOpt() {
OptimizationA oa;
return oa;
}
// 編譯器生成的代碼:(named return value (NRV))
// 分爲兩步操做
// 第一步:將上面的函數重寫爲下面的帶引用參數的形式
void GetOpt(OptimizationA &_result) {
OptimizationA oa;
//oa的一系列操做...
// 第二步:在return返回以前,調用__result的copy 構造函數
__result::OptimizationA::OptimizationA(oa);
return;
}
複製代碼
先來看段代碼:windows
class InitialzationB {
public:
// InitialzationB()
// {}
InitialzationB(int value): m_IA(value), m_a(value), m_b(value)
/* 放在初始化列中…… 1.若是是在成員列表初始化,站在編譯器的角度看 m_IA.InitialzationA::InitialzationA(value) */
{
/* 放在構造函數中….. m_IA = value; 2.若是是在函數內部初始化,站在編譯器的角度看 A.先是生成一個臨時對象 InitialzationA oc; oc.InitialzationA::InitialzationA(value); B.在m_IA的copy ctor m_IA.InitialzationA::InitialzationA(oc); C.臨時對象再去銷燬 oc.InitialzationA::~InitialzationA(); 因此成員變量初始化會提升效率,但只針對類類型變量,對基本類型無影響。 在初始化列表中,不要用類成員變量去初始化另一個成員變量 */
}
private:
InitialzationA m_IA; // 自定義class
int m_a;
int m_b;
};
複製代碼
InitialzationB(int value): m_IA(value), m_a(value), m_b(value) 這就是初始化列表的調用方法微信
簡單來講爲了初始化對象時的效率。看上面的代碼第7行放在初始化列中,從編譯器的角度看就是直接調用了InitialzationA的構造函數。可是你若是放在16行,那麼在編譯器的角度看就是先生成了一個InitialzationA臨時對象,在調用m_IA的copy構造函數,而後臨時對象的消亡調用析構函數。因此大費周章的構造對象形成效率的降低。 調用時機:編譯器會在構造函數以前會插入一段額外的代碼,這就是initialization list。而後在執行用戶寫的代碼。函數
1. 成員變量是個引用 |
2. 成員變量是const類型 |
3. 成員變量是帶參數的構造函數類類型 |
4. 基類有帶參數的構造函數 |
初始化順序是按照在類中的聲明順序的來決定。因此在類的初始化列表中仍是嚴格按照類中聲明的順序來複制。測試
好比:優化
class InitialzationB {
public:
// InitialzationB()
// {}
// InitialzationB(int value): m_IA(value) , m_b(value), m_a(m_b)
// 正宗作法
InitialzationB(int value): m_IA(value), m_a(value), m_b(value)
{
}
private:
InitialzationA m_IA;
int m_b;
int m_a;
};
複製代碼
不要在初始化列表中調用成員函數,由於你不知道這個函數之後會多麼的依賴當前的對象。spa
如今咱們開始回答上面提出的問題,第一個方法至少消耗了一個ctor,copy ctor, dtor,同時還要考慮編譯器的實現,中間可能還會temp object的生成,又會增長一個copy ctor,dtor。反過來再看方法二隻消耗了ctor,dtor。效率確定比方法一高。 知道了編譯器作了什麼,和怎麼作的。這將有助於對C++語言背後的實現細節更瞭若指掌,才能寫出高效的程序。同時也看出來c++爲了追求效率,背後作了不少咱們不知道的事情。最後假如咱們是編譯器,咱們會如何生成代碼的?這是值得咱們思考的地方。翻譯