定义
使用初始化列表的原因
必须使用初始化列表的时候
成员变量的顺序
链接添加链接描述
这里对列表初始化做个总结:
类中的成员变量只是一个声明,真正的定义在列表初始化,即使你不写列表初始化,默认有一个列表初始化。
如果你不使用列表初始化,在构造函数内部对成员变量进行赋值,那么其实就是构造函数,然后使用赋值运算。
具体我们来看下面的这个例子:
class Test1 {
public:
Test1() // 无参构造函数
{
std::cout << "Construct Test1" << std::endl;
}
Test1(const Test1 &t1) // 拷贝构造函数
{
std::cout << "Copy constructor for Test1" << std::endl;
this->a = t1.a;
}
Test1 &operator=(const Test1 &t1) // 赋值运算符
{
std::cout << "assignment for Test1" << std::endl;
this->a = t1.a;
return *this;
}
int a;
};
class Test2 {
public:
Test1 test1;
Test2() {}
Test2(Test1 &t1) { test1 = t1; }
// Test2(Test1 &t1) : test1(t1) {}
};
int main() {
Test1 test1;
Test2 test2(test1);
}
我们先看无初始化列表的打印情况:
Construct Test1 Construct Test1 assignment for Test1
这里出现了两次无参构造和一次赋值运算,为什么会出现两次无参构造呢?
这是因为Test2使用默认的初始化列表对test1进行空间分配,调用了一次,然后又调用了一次拷贝构造,再加上上面Test1本身的一次,所以出现了两次无参构造。
这次我们将Test2改一下:
class Test2 {
public:
Test1 test1;
Test2() {}
Test2(Test1 &t1) : test1(t1) {}
};
再次执行一次上面的main函数:
Construct Test1 Copy constructor for Test1
这次只有一次无参构造,一次拷贝构造,这是因为这次用的是初始化列表,初始化列表中直接调用拷贝构造一次成形。
我们再次改动一下,将class Test2 恢复到构造函数内赋值,并且取消class Test1中的无参数构造函数,按照上面讲的,class Test2会在默认的初始化列表中调用class Test1的无参数构造(但是这次我们删除了class Test1的无参数构造,那么调用无参数构造的话就会一定失败报错),按照这个假设,我们实验一下:
class Test1 {
public:
//Test1() // 无参构造函数
//{
// std::cout << "Construct Test1" << std::endl;
//}
Test1(const Test1 &t1) // 拷贝构造函数
{
std::cout << "Copy constructor for Test1" << std::endl;
this->a = t1.a;
}
Test1 &operator=(const Test1 &t1) // 赋值运算符
{
std::cout << "assignment for Test1" << std::endl;
this->a = t1.a;
return *this;
}
int a;
};
class Test2 {
public:
Test1 test1;
Test2() {}//报错
Test2(Test1 &t1) { test1 = t1; }//报错
// Test2(Test1 &t1) : test1(t1) {}
};
int main() {
Test1 test1;
Test2 test2(test1);
}
程序直接就报错了,并且class Test2的无参数构造也报错,为什么呢?
1、类中的成员变量只是声明,真正分配空间在初始化列表中;
2、初始化列表中赋值成员变量等价于调用拷贝构造函数。
3、对于const 引用类型的成员变量一定要在初始化列表中赋值。
最后区别一下 初始化列表和列表初始化
C语言中直接使用{}定义且赋值的操作其实就可以称之为列表初始化
struct Test_ST_A {
int num;
char *ptr;
float fnum;
};
class Test_Class_A {
public:
//virtual ~Test_Class_A() {};
//Test_Class_A() {};
int num;
char *ptr;
float fnum;
private:
//Test_Class_A(const Test_Class_A &) {};
};
int main() {
Test_ST_A st_a = { 0, NULL, 1.1};
Test_Class_A cla_a = { 0, NULL, 1.1};
return 0;
}
在C++中是可以直接使用{}包含多个参数直接对内部数据进行初始化的,并且这些参数的类型是可以不一致的;同时如果定义了构造函数、虚函数之类的则无法再这样初始化了,必须定义符合一致的Test_Class_A(int, const char* , float) {};构造函数方可。这就是列表初始化,调用的是构造函数哟
initializer_list
这个类主要是针对于容器来的,对于容器来说也应当想C语言中数组一样提供类似的初始化、赋值函数
int main() {
std::vector v = {1, 2, 3};
return 0;
}
这里可以当作简单的语法糖,但是当我们期望传入一个{}()来初始化一个类内部的vector该如何呢?
class Test_Class_A {
public:
//virtual ~Test_Class_A() {};
Test_Class_A(std::initializer_list l): data(l) {
};
std::vector data;
};
int main() {
Test_Class_A tca = { 1, 2, 3};
return 0;
}
如果一个类我们如下:
class Test1 {
public:
// Test1() // 无参构造函数
// {
// std::cout << "Construct Test1" << std::endl;
// }
Test1(const Test1 &t1) // 拷贝构造函数
{
std::cout << "Copy constructor for Test1" << std::endl;
this->a = t1.a;
}
Test1 &operator=(const Test1 &t1) // 赋值运算符
{
std::cout << "assignment for Test1" << std::endl;
this->a = t1.a;
return *this;
}
int a;
};
我们把无参数构造注释掉,我们在测试类中定义:
class Test2 {
public:
Test1 test1{};
}
编译的时候就会发现报错,为什么呢,这是因为在初始化列表中调用了无参数构造,但是Test1却没有无参数构造,所以报错。
也就是说,在类成员变量中加出列化列表这是告诉编译器,这些参数我要在调用默认初始化列表中调用这些参数的构造函数。



