构造函数初始化列表
以下所有程序在code::blocks中编译运行,使用GNU GCC compiler
一、什么是构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:
class MyClass
{
public:
MyClass():x(1),y(2){}
private:
int x;
int y;
};
MyClass():x(1), y(2){}就是构造函数初始化列表,分别将MyClass的两个成员变量x和y初始化为1和2.
以上的构造函数初始化列表跟普通的构造函数:MyClass(){x = 1; y = 2;}效果相同。
二、使用构造函数初始化列表的原因
使用构造函数初始化列表有以下两个原因:
1.必要性
(1)类中某一成员的类型是没有默认构造函数的类(或结构体)
如果类中存在这样一个成员,它的类型是没有默认构造函数(就是只有带参数的构造函数)的类或结构体,这时要对这个类成员进行初始化就只能调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成这一步。
错例:
classMyClassA
{
public:
MyClassA(int a){x = a;} //带参数的构造函数
int x;
};
classMyClassB
{
public:
MyClassA ca;
};
报错:error: no matching function for call to'MyClassA::MyClassA()'|
就是说MyClassA缺少不带参数的默认构造函数MyClassA::MyClassA()
为了验证,可以为MyClassA补上MyClassA(),编译通过。
还可以通过构造函数初始化列表来解决这一问题:
正确例程:
classMyClassA
{
public:
MyClassA(int a){x = a;} //带参数的构造函数
int x;
};
classMyClassB
{
public:
MyClassB():ca(111){}; //构造函数初始化列表
MyClassA ca;
};
这时x初始化为111.
还有以下情况需要用到构造函数初始化列表。
(2)类中含有const成员或引用类型成员
当类成员中含有const或是引用类型成员时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。
错误例子:
classMyClassA
{
public:
MyClassA(){x = 1;}
const int x;
};
编译报错:
1.error: uninitialized member'MyClassA::x' with 'const' type 'const int'|(x作为常整型变量没有经过初始化)。
2.error: assignment of read-onlydata-member 'MyClassA::x'|(错误:构造函数中对只读变量x进行赋值操作)
要解决以上问题,可以使用初始化列表。
正确例子:
classMyClassA
{
public:
MyClassA():x(1){}; //构造函数初始化列表
const int x;
};
对于引用型变量,亦如是。
2.效率性
类对象要通过构造函数构造,对于没有初始化列表的构造函数,进入构造函数体后进行的是赋值操作。赋值和初始化是不同的,这里存在着效率的差异,如果不用成员初始化列表,那么类对自己的类成员分别进行一次隐式的默认构造函数调用,和一次复制操作符调用,这样做效率就得不到保障。
注意:构造函数需要初始化的数据成员,不论是否出现在构造函数的成员初始化列表中,都会在该处完成初始化,并且初始化的顺序和其在声明时的顺序是一致的,与列表的先后顺序无关,所以要特别注意,保证两者顺序一致才能真正保证其效率。
程序:
#include<iostream>
usingnamespace std;
classMyClassA
{
public:
MyClassA():y(1), x(2){}; //构造函数初始化列表
int x;
int y;
};
intmain()
{
MyClassA ca;
cout << "x = " <<ca.x << "; y = " << ca.y << endl;
return 0;
}
没有errors,但是会出现warning:
'MyClassA::y' will be initialized after 'intMyClassA::x' when initialized here|
可以看到,即使初始化列表中y在x前,但是,初始化时依旧按照声明时的顺序:先x后y。
再看一个很具启发性意义的程序:
#include<iostream>
usingnamespace std;
classMyClassA
{
public:
MyClassA(int a, int b); //构造函数
int x;
int y;
};
MyClassA::MyClassA(inta, int b):y(a), x(y){}; //带有初始化列表
intmain()
{
MyClassA ca(2, 4);
cout << "x = " <<ca.x << "; y = " << ca.y << endl;
return 0;
}
猜猜输出结果是什么?x = 4273110; y = 2。实际上,y的值时确定的为2,但是x的值是不确定的!
成员初始化列表中y前x后,而且x初始化为和y一样,但是从输出结果看来,明显没有达到此目的。实际上,初始化时是按照声明顺序x前y后的,所以先执行x(y),但是此时y的值不确定,所以x的值就不确定了。接着执行y(a),于是y初始化的值为2.
如果让初始化列表中数据成员出现的顺序和声明时的顺序一样,问题就迎刃而解。见以下程序:
#include<iostream>
usingnamespace std;
classMyClassA
{
public:
MyClassA(int a, int b); //构造函数
int x;
int y;
};
MyClassA::MyClassA(inta, int b):x(a), y(x){}; //带有初始化列表
intmain()
{
MyClassA ca(2, 4);
cout << "x = " <<ca.x << "; y = " << ca.y << endl;
return 0;
}
输出:x = 2; y = 2