const成员的初始化只能在构造函数初始化列表中进行
引用成员的初始化也只能在构造函数初始化列表中进行
对象成员(对象成员所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行
(这个列表并不决定类中成员初始化的顺序,因为初始化顺序总是按照成员在类定义中的声明顺序进行)
#include <iostream> using namespace std;class A {public : A (int a) : _a(a), _p(nullptr ) { } private : int _a; int *_p; }; int main () { A aa (10 ) ; return 0 ; }
类之间嵌套
使用初始化列表
class Animal {public : Animal () { std::cout << "Animal() is called" << std::endl; } Animal (const Animal &) { std::cout << "Animal (const Animal &) is called" << std::endl; } Animal &operator =(const Animal &) { std::cout << "Animal & operator=(const Animal &) is called" << std::endl; return *this ; } ~Animal () { std::cout << "~Animal() is called" << std::endl; } }; class Dog {public : Dog (const Animal &animal) : __animal(animal) { std::cout << "Dog(const Animal &animal) is called" << std::endl; } ~Dog () { std::cout << "~Dog() is called" << std::endl; } private : Animal __animal; }; int main () { Animal animal; std::cout << std::endl; Dog d (animal) ; std::cout << std::endl; return 0 ; }
运行结果:
Animal () is called Animal (const Animal &) is calledDog (const Animal &animal) is called~Dog () is called ~Animal () is called ~Animal () is called
依次分析从上到下:
main函数中Animal animal;
调用默认构造。
Dog d(animal);
等价于:
Animal __animal = animal;
实际上就是调用了拷贝构造,因此输出了:
Animal (const Animal &) is called
再然后打印Dog的构造函数里面的输出。
最后调用析构,程序结束。
构造函数赋值初始化对象
构造函数修改如下:
Dog (const Animal &animal) { __animal = animal; std::cout << "Dog(const Animal &animal) is called" << std::endl; }
此时输出结果:
Animal () is called Animal () is calledAnimal & operator =(const Animal &) is called Dog (const Animal &animal) is called~Dog () is called ~Animal () is called ~Animal () is called
于是得出:
当调用Dog d(animal);
时,等价于:
先定义对象,再进行赋值,因此先调用了默认构造,再调用=操作符重载函数。
Animal __animal; __animal = animal;
无默认构造函数的继承关系中
现考虑把上述的关系改为继承,并修改Animal与Dog的构造函数,如下代码:
class Animal {public : Animal (int age) { std::cout << "Animal(int age) is called" << std::endl; } Animal (const Animal & animal) { std::cout << "Animal (const Animal &) is called" << std::endl; } Animal &operator =(const Animal & amimal) { std::cout << "Animal & operator=(const Animal &) is called" << std::endl; return *this ; } ~Animal () { std::cout << "~Animal() is called" << std::endl; } }; class Dog : Animal {public : Dog (int age) : Animal (age) { std::cout << "Dog(int age) is called" << std::endl; } ~Dog () { std::cout << "~Dog() is called" << std::endl; } };
上述是通过初始化列表给基类带参构造传递参数,如果不通过初始化列表传递,会发生什么影响?
去掉初始化列表
Dog (int age) { std::cout << "Dog(int age) is called" << std::endl; }
运行程序:
error: no matching function for call to ‘Animal::Animal ()’
由于在Animal中没有默认构造函数,所以报错,遇到这种问题属于灾难性的,我们应该尽量避免,可以通过初始化列表给基类的构造初始化。
类中const数据成员、引用数据成员
特别是引用数据成员,必须用初始化列表初始化,而不能通过赋值初始化!
例如:在上述的Animal中添加私有成员,并修改构造函数:
class Animal {public : Animal (int age,std::string name) { std::cout << "Animal(int age) is called" << std::endl; } private : int &age_; const std::string name_; };
报下面错误:
error: uninitialized reference member in ‘int&’
应该改为下面:
Animal (int age, std::string name) : age_ (age), name_ (name) { std::cout << "Animal(int age) is called" << std::endl; }
聚合初始化
聚合初始化是一种为类类型(如结构体 struct
或联合体 union
)提供的列表初始化形式,常用于简单的数据结构。为了使类或结构体符合聚合初始化的条件,必须满足以下要求:
无私有或受保护的非静态数据成员 :所有数据成员必须是公共的。
没有用户声明的构造函数 :类或结构体中不能定义构造函数。
没有虚函数 :类中不能有虚函数。
没有继承的构造函数(自 C++17 起) :不能继承构造函数或有私有、受保护的基类。
无默认成员初始化器(C++11 起) :数据成员不能有默认初始化器。
结构体的聚合初始化:
#include <iostream> using namespace std;struct Point { int x; int y; }; int main () { Point p1 = {10 , 20 }; cout << "Point: (" << p1.x << ", " << p1.y << ")" << endl; return 0 ; }
Point
结构体没有构造函数、私有成员或虚函数,因此可以使用 {}
进行聚合初始化。
类的聚合初始化:
#include <iostream> using namespace std;class Rectangle {public : int width; int height; }; int main () { Rectangle r1 = {30 , 40 }; cout << "Rectangle: " << r1.width << " x " << r1.height << endl; return 0 ; }
虽然 Rectangle
是类,但它满足聚合类型的条件(没有构造函数、私有成员等),因此也可以使用聚合初始化。
联合体的聚合初始化:
#include <iostream> using namespace std;union Data { int intVal; float floatVal; }; int main () { Data d = {42 }; cout << "Data: " << d.intVal << endl; return 0 ; }
Data
是一个联合体,符合聚合初始化的条件,因此可以直接用大括号初始化。