C++类的初始化列表是怎么一回事?哪些东西必须放在初始化列表中进行初始化,原因是什么?

目录

  • 01-C++类的初始化列表的概要介绍
    • 语法
    • 为什么使用初始化列表?
    • 示例代码
    • 小结
  • 02-初始化列表是写在构造函数的函数体之前的代码的
    • **1. 结构分析**
    • **2. 示例:基类 + 成员变量**
        • **输出**
    • **3. 初始化列表 vs 构造函数体**
      • **(1) 使用初始化列表**
      • **(2) 在构造函数体赋值**
  • 03-必须放在初始化列表中进行初始化的东西
    • 常量成员
    • 引用成员
    • 没有默认构造函数的类成员
    • 类的基类没有默认构造函数

01-C++类的初始化列表的概要介绍

在C++中,类的初始化列表是一个在构造函数体之前用来初始化类成员的机制。它允许你在构造函数的声明部分直接初始化成员变量,而不是在构造函数体内通过赋值的方式。

语法

初始化列表紧跟在构造函数名称之后,用冒号 : 开始,后面跟着成员变量的初始化。每个成员变量通过逗号 , 分隔。

class ClassName {
public:
    // 构造函数带有初始化列表
    ClassName(int a, int b) : x(a), y(b) {  // x和y分别通过初始化列表初始化
        // 其他构造函数体代码(如果有)
    }

private:
    int x, y;
};

其中x(a)的意思并不是调用名叫x的函数,而是把成员变量x的值初始化a的值。同样,y(b)的意思也并不是调用名叫y的函数,而是把成员变量y的值初始化为b的值。

为什么使用初始化列表?

  1. 提高效率:通过初始化列表可以直接初始化类成员,而不是先调用默认构造函数再进行赋值操作。
  2. 有一些成员变量只能在初始化列表中进行初始化,具体如下:
    ①常量成员和引用成员:常量成员变量和引用成员必须通过初始化列表来初始化,因为它们无法在构造函数体内被赋值。至于为什么,请往后看,我在后面说明了原因。
    ②如果类型为类类型的成员变量【即某个类的实例对象】没有默认构造函数,也需要在初始化列表中进行初始化。关于什么叫默认构造函数,请看我的另一篇博文 [https://blog.csdn.net/wenhao_ir/article/details/145428240]
  3. 如果一个类的基类没有使用默认构造函数,也需要在初始化列表进行初始化。关于什么叫默认构造函数,请看我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145428240

示例代码

#include 
using namespace std;

class Rectangle {
public:
    // 带有初始化列表的构造函数
    Rectangle(int l, int w) : length(l), width(w) {
        // 构造函数体
        cout << "Rectangle created!" << endl;
    }

    // 打印矩形的面积
    void area() {
        cout << "Area: " << length * width << endl;
    }

private:
    int length, width;
};

int main() {
    Rectangle rect(10, 5);  // 创建一个长为10,宽为5的矩形
    rect.area();  // 打印矩形的面积
    return 0;
}

关键点:

  1. Rectangle(int l, int w) : length(l), width(w):初始化列表在这里将 lengthwidth 直接初始化为传入的 lw 参数的值。
  2. lengthwidth 是在构造函数体内使用之前就已经初始化了,而不是在函数体内通过赋值。

小结

  • 初始化列表通过冒号 : 开始,直接在构造函数声明中初始化成员变量。
  • 它比在构造函数体内赋值更高效,尤其对于常量成员、引用成员或需要特殊初始化的成员非常有用。

02-初始化列表是写在构造函数的函数体之前的代码的

初始化列表(Initializer List)是写在构造函数的函数体 {} 之前,用于初始化成员变量和基类。它的语法如下:

ClassName::ClassName(参数列表) : 成员变量1(1), 成员变量2(2), 基类() {  
    // 构造函数体
}

1. 结构分析

class Example {
    int a;
    double b;
public:
    Example(int x, double y)  // 构造函数
        : a(x), b(y)  // ✅ 初始化列表(在函数体 `{}` 之前)
    {
        // 构造函数体
    }
};
  • : a(x), b(y)初始化列表,用来初始化 ab。其中 a(x)的意思并不是调用名叫a的函数,而是把成员变量a的值初始化x的值。同样,b(y)的意思也并不是调用名叫b的函数,而是把成员变量b的值初始化为y的值。

  • {} 内是构造函数体,在初始化列表执行完之后才会运行。


2. 示例:基类 + 成员变量

#include 

class Base {
public:
    Base(int x) { std::cout << "Base constructor: " << x << "\n"; }
};


class Derived : public Base {
    int a;
public:
    Derived(int x, int y)
        : Base(x), a(y)  // ✅ 初始化列表
    {
        std::cout << "Derived constructor: " << a << "\n";
    }
};


int main() {
    Derived d(10, 20);
}
输出
Base constructor: 10
Derived constructor: 20

流程

  1. 先执行初始化列表
    • Base(x) 先调用 Base 的构造函数,输出 "Base constructor: 10"
    • a(y) 赋值给成员变量 a即把y的值传递给成员变量a,注意这里不是调用函数名为a的函数。
  2. 然后执行构造函数体 {}
    • 输出 "Derived constructor: 20"

上面代码中类Derived在定义时继续了基类Base,那么请问public Base中public的意思是什么?
答案见:https://blog.csdn.net/wenhao_ir/article/details/145428506


3. 初始化列表 vs 构造函数体

(1) 使用初始化列表

class Example {
    int x;
public:
    Example(int value) : x(value) {}  // ✅ 直接初始化
};

(2) 在构造函数体赋值

class Example {
    int x;
public:
    Example(int value) { x = value; }  // 在构造函数中赋值,不推荐
};

推荐使用初始化列表,避免在构造函数中赋值,从而提高效率。


03-必须放在初始化列表中进行初始化的东西

常量成员

由于C++规定,所有成员变量必须在对象构造完成前初始化。而常量成员在初始化后不能修改,所以只能用初始化列表进行初始化。例子如下:

   class Test {
       const int x;
   public:
       Test(int val) : x(val) {} // 常量成员x必须用初始化列表进行初始化,否则编译错误
   };

引用成员

关于引用成员为什么也必须在初始化列表中进行初始化,详情见 https://blog.csdn.net/wenhao_ir/article/details/145422877
其实原因也和常量成员差不多,只是你需要了解下C++的引用成员是怎么回事儿。
例子如下:

   class Test {
       int& ref; // ref是一个引用类型的成员
   public:
       Test(int& r) : ref(r) {} // ref必须用初始化列表进行初始化
   };

没有默认构造函数的类成员

如果某个成员变量的类型为类类型,即这个成员变量是一个类的对象实例,而这个类又没有默认构造函数,那么也需要在初始化列表中进行初始化。
详情见 https://blog.csdn.net/wenhao_ir/article/details/145428240

类的基类没有默认构造函数

如果一个类的基类没有使用默认构造函数,也需要在初始化列表进行初始化。详情请请看我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145428240

你可能感兴趣的:(C/C++基础,c++)