在刚接触QT的时候我们都会看到自动生成的主界面构造函数如下:
MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
}
这边使用的就是初始化列表,初始化列表主要用于以下几种情况:
第一种:
类B中包含另一个类A的对象a,但是类A的构造函数含有参数例如A(int x),那么当类B初始化时,若没有初始化列表,无法正常初始化B,正确做法是将B的构造函数写为B():a(x)。
代码如下:
#include "iostream"
using namespace std;
class A
{
public:
A(int x){
cout << "A Create" << endl;
};
};
class B
{
public:
B():a(1){
cout << "B Create" << endl;
};
private:
A a;
};
int _tmain(int argc, _TCHAR* argv[])
{
B test;
std::getchar();
return 0;
}
运行效果:
A Create
B Create
如果将初始化列表a(1)删去,编译将出错。
第二种:
当类成员中含有const修饰的变量时,在声明时就需要初始化,在构造函数中只能进行赋值,并不是在初始化阶段,因此这种情况必须要用到初始化列表来初始化,示例代码如下:
#include "iostream"
using namespace std;
class A
{
private:
const int a;
public:
A() :a(1){
cout << "A Create" << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A testA;
std::getchar();
return 0;
}
运行效果:
A Create
如果将初始化列表a(1)删去,编译将出错,提示常量a没有初始化。
第三种:
当类成员中含有引用(&)的变量时,在声明时就需要初始化,在构造函数中只能进行赋值,并不是在初始化阶段,因此这种情况必须要用到初始化列表来初始化,示例代码如下:
using namespace std;
class B
{
private:
int &b;
public:
B(int b) :b(b){
cout << "B Create" << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
B testB(1);
std::getchar();
return 0;
}
运行效果:
B Create
如果将初始化列表b(b)删去,编译将出错,提示构造函数未提供&b的初始化设定。
第四种:
当类为继承类,且想要初始化父类成员时,需要使用初始化列表,示例代码如下:
using namespace std;
class A{
public:
A(){};
A(int x) :int_x(x) { cout << "Creat A" << endl; };
void printx(){
cout << int_x << endl;
}
int int_x;
};
class B :public A{
public:
B() :A(1){
//B() : int_x(1){//错误1
//A(1); //错误2
cout << "Creat B" << endl;
};
};
int _tmain(int argc, _TCHAR* argv[])
{
A *p = new B();
p->printx();
std::getchar();
return 0;
}
运行效果:
Creat A
Creat B
1
如果将B的构造函数初始化列表写为错误1,那么编译不通过,初始化冲突,到底是父类初始化还是子类初始化?
如果将父类A的构造函数显式调用即错误2,并且B构造函数不使用初始化列表,那么打印出的int_x将是随机值。原因在于在B执行构造函数的时候,已经隐式的调用A的无参构造函数,此时int_x已经被初始化,此时再次显示调用A的有参构造函数已经无法再次初始化int_x,因此并没有被赋值,所以此时打印出来的是随机值。
第五种:
需要提升编译效率的情况,初始化列表可以使得变量在初始化阶段的同时进行赋值,而不使用初始化列表的情况则需要进行初始化和赋值两个阶段,这对于大型类来说效率有很大的提升。
另外,需要注意的是,初始化列表并不是变量初始化顺序的依据,初始化顺序依然是以声明的顺序为准。