面向对象的编程——构造函数和析构函数(2)
作为一个菜鸟,我们所关注的一个核心问题是,在写构造函数时,使用初始化表和在构造函数内使用数据成员的赋值语句之间有什么异同?
先来看一段程序:
class A
{
private:
int b;
public:
A();
};
i. A::A():b(2)
{}
ii. A::A()
{
b=2;
}
运行后发现上述两种方式效果一样。但是,这并不代表这两种方式在编译过程中就一定一模一样。只不过我们很巧合的选取了一般的情况,即:普通的内置类型的初始化。事实上,第一种方式是初始化,第二种方式却是赋值,两种方式有很大的差别。我们再看两个例子:
例一
#include <iostream>
using namespace std;
class B
{
public:
B()
{
cout<<"B"<<endl;
}
};
class A
{
private:
int b;
public:
B bb;
A(B& b0):bb(b0)
{
cout<<"B2"<<endl;
}
};
int main()
{
B b1;
cout<<"B1"<<endl;
A a2(b1);
return 0;
}
运行结果如下:
例二
#include <iostream>
using namespace std;
class B
{
public:
B(){cout<<"B"<<endl;}
};
class A
{
private:
int b;
public:
B bb;
A(B& a0);
};
inline A::A(B& a0)
{
bb=a0;
cout<<"B3"<<endl;
}
int main()
{
B b1;
cout<<"B1"<<endl;
A a2(b1);
return 0;
}
运行情况如下:
可以看出,例二比例一多调用了一次B(),这是因为成员初始化表只提供该类数据成员的初始化,而在构造函数体内对数据成员设置值是一个赋值操作,区别的重要性取决于数据成员的类型。在上面这个例子中,bb的关联的缺省构造函数是:B(),用第一种方式写构造函数时可以避免出现在例二中bb赋值前先调用一下构造函数B()。
这是对于类对象的一个不同之处,在关于内置类型时,两种构造函数也有一个例外是不同的,就是当初始化的是常量或者引用时,只能用初始化列表形式的构造函数。
例:
class A
{
public:
const int a;
A():a(1){}
};
而不能写成:
class A
{
public:
const int a;
A();
};
inline A::A()
{
a=1;
}
总结如下:
初始化成员列表的构造函数和函数体内数据成员赋值的构造函数有两点不同:
1、内置类型和或者复合类型的话,效率上两者没有多大的差别,不同点只是当类型是const或者引用时只能用列表形式;
2、对于类对象的情况下,用初始化列表的话只需调用一次拷贝构造函数,而在构造函数内进行对象的赋值操作的话,会先调用缺省构造函数一次,再调用operator=赋值函数一次,由此可见效率的差别。