0

0

C++类成员初始化列表使用方法

P粉602998670

P粉602998670

发布时间:2025-09-16 13:48:02

|

233人浏览过

|

来源于php中文网

原创

C++类成员初始化列表在构造函数体执行前直接初始化成员,相比构造函数体内赋值更高效且必要用于const、引用及无默认构造函数的类类型成员;其初始化顺序由类中成员声明顺序决定,而非初始化列表中的书写顺序,需避免依赖未初始化成员的陷阱;C++11引入的类内初始化提供默认值,但成员初始化列表优先级更高,两者结合使用可提升代码简洁性与灵活性。

c++类成员初始化列表使用方法

C++类成员初始化列表是构造函数中初始化类成员变量的一种特殊语法结构,它在构造函数体执行之前,以直接初始化的方式为成员变量赋初值。这与在构造函数体内使用赋值操作符(

=
)初始化成员有着本质的区别,尤其在效率、强制性以及处理特定类型成员(如
const
成员、引用成员和没有默认构造函数的类类型成员)时,其重要性不言而喻。在我看来,理解并熟练运用成员初始化列表,是C++程序员迈向高效和正确编程的关键一步。

解决方案

要正确使用C++类成员初始化列表,你需要在构造函数的参数列表之后、构造函数体之前,用冒号

:
引出初始化列表。列表中的每个成员都通过其名称后跟括号内的初始化表达式来指定。

#include 
#include 
#include 

class MyClass {
public:
    int value;
    const int constValue; // const 成员
    std::string name;     // 类类型成员
    int& refValue;        // 引用成员
    std::vector data; // 另一个类类型成员

    // 构造函数使用成员初始化列表
    MyClass(int v, int cv, const std::string& n, int& rv)
        : value(v),          // 直接初始化 int
          constValue(cv),    // 必须通过初始化列表初始化 const 成员
          name(n),           // 直接初始化 std::string,避免默认构造后赋值
          refValue(rv),      // 必须通过初始化列表初始化引用成员
          data({1, 2, 3})    // 也可以使用列表初始化(C++11)
    {
        // 构造函数体在这里执行。
        // 此时,所有成员都已经被初始化完毕。
        std::cout << "MyClass 构造函数体执行。" << std::endl;
    }

    void print() const {
        std::cout << "Value: " << value
                  << ", ConstValue: " << constValue
                  << ", Name: " << name
                  << ", RefValue: " << refValue << std::endl;
    }
};

int main() {
    int externalRef = 100;
    MyClass obj(10, 20, "TestName", externalRef);
    obj.print();

    // 尝试修改 constValue 会报错
    // obj.constValue = 30; // 编译错误

    // 引用成员的改变会影响外部变量
    obj.refValue = 200;
    std::cout << "ExternalRef after obj.refValue change: " << externalRef << std::endl;

    return 0;
}

在这个例子中,

value
,
constValue
,
name
,
refValue
data
都通过初始化列表得到了初始化。注意,
constValue
refValue
必须在初始化列表中初始化,否则会引起编译错误
std::string name
也在初始化列表中初始化,这比在构造函数体内先默认构造再赋值要高效。

为什么C++推荐使用成员初始化列表,而非在构造函数体内赋值?

这其实是个很微妙但又极其重要的点,涉及到C++对象生命周期的底层机制和效率考量。我个人觉得,这不仅仅是“推荐”,在某些场景下,它甚至是“强制”的。

立即学习C++免费学习笔记(深入)”;

当你在构造函数体内对成员进行赋值操作时,比如

this->value = v;
,实际上发生了两步:

  1. 默认构造: 成员变量
    value
    在构造函数体执行前,会先调用其默认构造函数(如果是类类型),或者进行默认初始化(如果是内置类型,且没有显式初始化)。
  2. 赋值操作: 接着,在构造函数体内,再通过赋值操作符
    =
    v
    的值赋给
    value

想象一下

std::string name;
这个成员。如果在构造函数体内写
name = n;
,那么
name
会先被默认构造(可能分配一块小内存),然后
n
的内容再通过
operator=
赋值给
name
,这通常涉及到旧内存的释放和新内存的分配与拷贝。而如果使用初始化列表
name(n)
name
会直接使用
n
来构造,只进行一次内存分配和数据拷贝。对于复杂对象,这种差异在性能上是相当显著的,尤其是在循环或创建大量对象时,避免了不必要的开销。

更重要的是,对于

const
成员和引用成员,它们一旦被初始化就不能再被赋值修改。因此,它们压根就没有“赋值”这一说,只能在对象创建时通过初始化列表进行“初始化”。同理,如果一个类类型成员没有默认构造函数,那么它也必须通过初始化列表来提供构造参数,否则编译器不知道如何构造它。这些都是初始化列表的“强制性”体现。

成员初始化列表的初始化顺序是怎样的?常见的陷阱有哪些?

这是一个很多C++新手容易踩坑的地方,包括我自己在初学时也犯过类似的错误。成员初始化列表的初始化顺序不是你写在列表里的顺序,而是成员在类中声明的顺序。这一点非常关键!

看一个例子:

e网企业2.0
e网企业2.0

一款适用于中小企业自助建站程序,是c#与xml技术相结合的产物,支持动态设定二级栏目,采用了开放式架构,建站模版自由添加。程序整合了(单一文本,新闻列表,图片列表 ,在线订单, 文件下载 , 留言板)六类插件,以所见即所得的方式,将烦锁的建站过程简化到三步,使用户可以轻松上手。 管理后台:manage.aspx 初始密码均为admin

下载
class MyOrderClass {
public:
    int b;
    int a;

    MyOrderClass(int valA, int valB)
        : a(valA), // 看起来 a 先被初始化
          b(valB)  // 看起来 b 后被初始化
    {
        std::cout << "a: " << a << ", b: " << b << std::endl;
    }
};

class PitfallClass {
public:
    int b;
    int a; // a 在 b 之后声明

    PitfallClass(int valA, int valB)
        : a(valA),
          b(a + valB) // b 尝试使用 a 的值
    {
        std::cout << "a: " << a << ", b: " << b << std::endl;
    }
};

int main() {
    MyOrderClass mo(10, 20); // 输出 a: 10, b: 20,看起来没问题

    // 陷阱在这里
    PitfallClass pc(10, 20); // 预期 a: 10, b: 30。实际输出可能 a: 10, b: 随机值 + 20
                             // 因为 b 在 a 之前声明,b 初始化时 a 尚未被初始化!
    return 0;
}

PitfallClass
中,
b
a
之前声明。因此,即使在初始化列表中
a(valA)
写在
b(a + valB)
前面,实际执行时,
b
会先被初始化。当
b(a + valB)
执行时,
a
还没有被
valA
初始化,它的值是一个未定义的值(可能是垃圾值)。这会导致
b
的值也是未定义的,这是一种典型的未定义行为。

所以,一个非常重要的实践是:永远按照成员在类中声明的顺序来编写初始化列表。这不仅能避免这种陷阱,也能让代码更清晰、更易于维护。编译器通常会对此发出警告,但最好还是从编码习惯上避免。

在现代C++中,成员初始化列表与类内初始化(In-class Initializers)有何异同?

C++11引入了类内初始化(In-class Initializers),这给成员初始化带来了更多的灵活性,也让很多初学者感到有些困惑,不知道何时该用哪个。在我看来,它们是互补而非替代的关系。

类内初始化(In-class Initializers): 你可以在类的定义中直接为非静态数据成员提供一个默认的初始化表达式。

class ModernClass {
public:
    int value = 0; // 类内初始化
    std::string name = "DefaultName"; // 类内初始化
    std::vector data{10, 20}; // 也可以用列表初始化语法

    // 如果没有提供构造函数,这些默认值就会被使用
    ModernClass() = default;

    // 如果提供了构造函数,并且构造函数没有在初始化列表中显式初始化这些成员,
    // 那么类内初始化器也会被使用。
    ModernClass(int v) : value(v) {
        // name 和 data 会使用它们的类内初始化器
    }
};

异同点

  1. 默认值 vs. 参数化值

    • 类内初始化主要用于为成员提供一个默认值。如果构造函数不显式初始化某个成员,就会使用这个类内值。
    • 成员初始化列表用于在构造时根据构造函数参数来初始化成员,提供更灵活、动态的初始化。
  2. 优先级

    • 如果一个成员同时有类内初始化器成员初始化列表中的初始化,成员初始化列表会优先。类内初始化器会被忽略。这挺有意思的,相当于给了一个“兜底”的默认值,但如果构造函数有更明确的指示,就听构造函数的。
  3. 强制性

    • const
      成员和引用成员不能通过类内初始化器初始化(
      const
      成员可以,但其值必须是常量表达式)。它们通常仍需要成员初始化列表来绑定到构造函数参数。
    • 对于没有默认构造函数的类类型成员,如果其构造参数是固定的常量,可以用类内初始化器。但如果参数需要从构造函数传入,则必须使用成员初始化列表。

何时使用

  • 使用类内初始化器:当成员有一个合理的、固定的默认值,并且你希望减少构造函数中的重复代码时。这对于那些不总是需要通过构造函数参数初始化的成员非常方便。
  • 使用成员初始化列表:当成员的初始化值依赖于构造函数的参数,或者成员是
    const
    、引用类型,以及没有默认构造函数的类类型时。它提供了精确控制成员初始化行为的能力。

在我看来,现代C++编程中,最佳实践往往是结合使用这两种方式。为那些有通用默认值的成员使用类内初始化器,而将那些依赖于构造函数参数或有特殊初始化要求的成员留给成员初始化列表处理。这样既能保持代码简洁,又能确保灵活性和正确性。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1465

2023.10.24

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

523

2023.09.20

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

465

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

12

2025.12.06

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

4

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

1

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

10

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

33

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

相关下载

更多

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 1.3万人学习

Rust 教程
Rust 教程

共28课时 | 4.4万人学习

Git 教程
Git 教程

共21课时 | 2.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号