构造函数的成员初始化列表 (一)

但是C++11规定,类的声明中可以给非静态成员变量赋初值:

1
2
3
4
5
class Base
{
public:
int m = 3;
};

类中的const成员进行初始化必须用这种方式

1
2
3
4
5
6
7
8
9
10
11
12
class Father {
public:
explicit Father(int father):
f(father),
m(10)
{

}
private:
const int f;
const int m;
};

如果不用这种方式初始化,会编译报错:error: C2789:必须初始化常量限定类型的对象

引用成员变量必须在列表中初始化

比如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base
{
public:
Base();
explicit Base(int a);
private:
int m=4; // 类体内赋值,只适用于C++11
const int abc;
int& f;
};

Base::Base(int a)
:abc(10), // 常量
f(m) //引用
{
}

如果没有在构造函数成员列表初始化,会报错error: uninitialized reference member in ‘int&’ ‘const int’ [-fpermissive]
也就是上面的1和2的情况

类中的引用必须用列表初始化的方式赋值,不能在类体内或构造函数中用=赋值,否则会报错:operator= is private

两个类没有继承关系,但是类B的对象是类A成员变量,而B的构造函数都是有参数的,而且参数没有默认值,此时必须在类A构造函数中对B初始化

举例:Father类跟上面一样,增加类MyClass如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyClass
{
public:
explicit MyClass()
:father(s)
{

}
private:
Father father; // 没有默认构造行为的成员
int s;
};
class Father
{
public:
Father(int a){cout<<"Father construct"<<endl;}
};

MyClass构造函数前,先运行Father的构造函数,所以需要对其初始化,否则报错。这里实际上用到了类的隐式转换。
只要Father类有一个函数是无参数的,那就不需要在MyClass类中对father显式初始化,让编译器自动完成即可。其实就是初始化Father构造函数的参数。

我们有更好的优化方法:把father改成指针类型,实现对此规则的优化,这也是Qt中的常见方法
。 因为定义在heap上的类的指针是不会运行构造函数的,类的对象要运行构造函数,然后用成员初始化列表的方法对指针初始化:

1
2
3
4
5
6
7
8
9
10
11
12
class MyClass
{
public:
explicit MyClass()
:father(new Father(s))
{

}
private:
Father* father;
int s;
};

类似的,Qt中的ui指针所指对象的初始化可以放到构造函数里:

1
2
3
4
5
6
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
ui = new Ui::MainWindow;
ui->setupUi(this);
}

构造函数中初始化的顺序

初始化的顺序与列表中的参数顺序无关,而是由类中定义的顺序决定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Base::Base(int a)
{
std::cout<<"constructor Base:"<<a<<endl;
}

class Derive : public Base
{
public:
Derive();
Base a;
Base b;
};
Derive::Derive():
b(8),
a(4)
{
}

Derive* d = new Derive();

运行结果是先a后b:
1
2
constructor Base:4
constructor Base:8

与初始化时a和b的顺序无关,只跟类Derive中的声明顺序有关。

静态变量不能用构造函数成员列表初始化

静态变量不属于某个对象,而是被类共有,假如构造函数能初始化,会造成变量值不同。编译时报错:error: C2438 无法通过构造函数初始化静态类数据