类的初始化列表如何提高程序效率 (一)

2014-11-24 00:59:33 · 作者: · 浏览: 11

1:类的初始化列表是怎么提高效率的?
2:类的初始化列表是按照列表中出现的顺序来初始化的吗?
看下面的两个程序代码:
程序一

[cpp]
class A
{
public:
A(int t=0)
{
cout<<"construct A"< }
A(const A &)
{
cout<<"copy A"< }
A & operator =(const A &)
{
cout<<"equal"< return *this;
}
~A()
{
cout<<"destory A"< }
};
class B
{
public:
A a;
B()
{
a=0;
}
};
int main()
{
B b;
return 0;
}

class A
{
public:
A(int t=0)
{
cout<<"construct A"< }
A(const A &)
{
cout<<"copy A"< }
A & operator =(const A &)
{
cout<<"equal"< return *this;
}
~A()
{
cout<<"destory A"< }
};
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"< }
~A()
{
cout<<"destory A"< }
};
class B
{
public:
A a;
B():a(0)
{
}
};
int main()
{
B b;
return 0;
}

class A
{
public:
A(int t=0)
{
cout<<"construct A"< }
~A()
{
cout<<"destory A"< }
};
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归为一类,为什