C++初始化列表与赋值

  • 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 called
Dog(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 called
Animal & 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;
  • 类中包含其他自定义的class或者struct,采用初始化列表,实际上就是创建对象同时并初始化

  • 而采用类中赋值方式,等价于先定义对象,再进行赋值,一般会先调用默认构造,在调用=操作符重载函数。

无默认构造函数的继承关系中

现考虑把上述的关系改为继承,并修改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)提供的列表初始化形式,常用于简单的数据结构。为了使类或结构体符合聚合初始化的条件,必须满足以下要求:

  1. 无私有或受保护的非静态数据成员:所有数据成员必须是公共的。

  2. 没有用户声明的构造函数:类或结构体中不能定义构造函数。

  3. 没有虚函数:类中不能有虚函数。

  4. 没有继承的构造函数(自 C++17 起):不能继承构造函数或有私有、受保护的基类。

  5. 无默认成员初始化器(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 是一个联合体,符合聚合初始化的条件,因此可以直接用大括号初始化。

------------------------------- 本文结束啦❤感谢您阅读-------------------------------
赞赏一杯咖啡