1:類的初始化列表是怎麼提高效率的?
2:類的初始化列表是按照列表中出現的順序來初始化的嗎?
看下面的兩個程序代碼:
程序一
[cpp]
class A
{
public:
A(int t=0)
{
cout<<"construct A"<<endl;
}
A(const A &)
{
cout<<"copy A"<<endl;
}
A & operator =(const A &)
{
cout<<"equal"<<endl;
return *this;
}
~A()
{
cout<<"destory A"<<endl;
}
};
class B
{
public:
A a;
B()
{
a=0;
}
};
int main()
{
B b;
return 0;
}
class A
{
public:
A(int t=0)
{
cout<<"construct A"<<endl;
}
A(const A &)
{
cout<<"copy A"<<endl;
}
A & operator =(const A &)
{
cout<<"equal"<<endl;
return *this;
}
~A()
{
cout<<"destory A"<<endl;
}
};
class B
{
public:
A a;
B()
{
a=0;
}
};
int main()
{
B b;
return 0;
}程序二
[cpp]
class A
{
public:
A(int t=0)
{
cout<<"construct A"<<endl;
}
~A()
{
cout<<"destory A"<<endl;
}
};
class B
{
public:
A a;
B():a(0)
{
}
};
int main()
{
B b;
return 0;
}
class A
{
public:
A(int t=0)
{
cout<<"construct A"<<endl;
}
~A()
{
cout<<"destory A"<<endl;
}
};
class B
{
public:
A a;
B():a(0)
{
}
};
int main()
{
B b;
return 0;
}程序一與程序二的運行結果是不同的,程序一中多了一次構造與析構函數,賦值函數的調用,這就是初始化列表提高效率的地方
程序一在編譯器器層被轉化成了
[cpp]
B()
{
//以下代碼不可實際運行,只是說明問題
a.A::A(0);//調用A的構造函數
A tmp(0);//產生一個臨時的對象
a=tmp;//通過賦值運算符將臨時對象賦給a
tmp.A::~A;//釋放臨時對象的空間
}
B()
{
//以下代碼不可實際運行,只是說明問題
a.A::A(0);//調用A的構造函數
A tmp(0);//產生一個臨時的對象
a=tmp;//通過賦值運算符將臨時對象賦給a
tmp.A::~A;//釋放臨時對象的空間
}下面是相應的匯編代碼:可以發現和上面的過程分析是一樣的,有興趣可以自已研究下
[cpp]
00401227 call @ILT+15(A::A) (00401014)
0040122C mov dword ptr [ebp-4],0
29: {
30: a=0;
00401233 push 0
00401235 lea ecx,[ebp-14h]
00401238 call @ILT+15(A::A) (00401014)
0040123D mov byte ptr [ebp-4],1
00401241 lea eax,[ebp-14h]
00401244 push eax
00401245 mov ecx,dword ptr [ebp-10h]
00401248 call @ILT+0(A::operator=) (00401005)
0040124D mov byte ptr [ebp-4],0
00401251 lea ecx,[ebp-14h]
00401254 call @ILT+120(A::~A) (0040107d)
31: }
00401227 call @ILT+15(A::A) (00401014)
0040122C mov dword ptr [ebp-4],0
29: {
30: a=0;
00401233 push 0
00401235 lea ecx,[ebp-14h]
00401238 call @ILT+15(A::A) (00401014)
0040123D mov byte ptr [ebp-4],1
00401241 lea eax,[ebp-14h]
00401244 push eax
00401245 mov ecx,dword ptr [ebp-10h]
00401248 call @ILT+0(A::operator=) (00401005)
0040124D mov byte ptr [ebp-4],0
00401251 lea ecx,[ebp-14h]
00401254 call @ILT+120(A::~A) (0040107d)
31: }程序二在底層被轉化成了[cpp] view plaincopyprint?
B():a(0)
{
a.A::A(0);
}
B():a(0)
{
a.A::A(0);
}可以發現通過初始化列表在申請了對象a的空間後直接調用初始化列表中指定的構造函數來構造成員對象,因此少了很多中間步驟,因此提高了程序的效率:)
這是程序二對應的匯編代碼(看看就知道了吧:)):
[cpp]
0040120A mov dword ptr [ebp-4],ecx
0040120D push 0
0040120F mov ecx,dword ptr [ebp-4]
00401212 call @ILT+10(A::A) (0040100f)
0040120A mov dword ptr [ebp-4],ecx
0040120D push 0
0040120F mov ecx,dword ptr [ebp-4]
00401212 call @ILT+10(A::A) (0040100f)到這了,初始化列表確實能夠提高程序的效率,不是嗎:),初始化列表就是指明了應該調用成員對象的哪個構造函數
對於第二個問題並不是按初始化列表來構造成員的,就不做實驗了,有興趣的可一塊討論,構造函數初始化的順序是按這個順序,先根據基類的聲明順序,調用基類的構造函數,初始化基類,再按照類裡面成員對象的聲明順序調用成員對象的構造函數對成員對象進行初始化:)
以下幾種情況一定要用到初始化列表
1:當初始化一個引用成員對象時
2:當初始化一個const成員對象時(1、2兩種情況可以想想為什麼?原理是一樣的,注意強調的是對象)
3:當調用一個基類的構造函數,而它擁有一組帶參數的構造函數時
4:當類裡面的成員對象有自已的構造函數時,且有參數時(3、4歸為一類,為什麼必須使用,原理也是一樣的)