当前位置: 首页 > article >正文

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 <iostream>
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 <iostream>

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


http://www.kler.cn/a/531438.html

相关文章:

  • pytorch实现变分自编码器
  • deepseek接入pycharm 进行AI编程
  • 【hot100】刷题记录(12)-回文链表
  • 【单层神经网络】基于MXNet库简化实现线性回归
  • Windows编译FreeRDP步骤
  • Apache Hudi数据湖技术应用在网络打车系统中的系统架构设计、软硬件配置、软件技术栈、具体实现流程和关键代码
  • MySQL(高级特性篇) 13 章——事务基础知识
  • Docker 部署 ClickHouse 教程
  • 搜索插入位置(35)
  • SpringBoot整合Mybatis|入门级增删改查|2025
  • Chromium132 编译指南 - Android 篇(五):获取源码
  • 八. Spring Boot2 整合连接 Redis(超详细剖析)
  • 自动化软件测试的基本流程
  • Ubuntu20安装docker
  • 【DeepSeek论文精读】6. DeepSeek R1:通过强化学习激发大语言模型的推理能力
  • coze扣子新一代AI应用开发平台
  • 利用matlab寻找矩阵中最大值及其位置
  • 详解分布式锁
  • 在K8S中,有哪几种控制器类型?
  • 深度学习 Pytorch 基础网络手动搭建与快速实现
  • 【Linux】24.进程信号(1)
  • Array.prototype 方法在复杂数据处理中的应用
  • 深入解析 posix_spawn():高效的进程创建方式(中英双语)
  • Spark的基本概念
  • 如何选择Spring AOP的动态代理?JDK与CGLIB的适用场景?
  • 42【文件名的编码规则】