LiSheng's blog LiSheng's blog
首页
笔记
个人简历
随笔集
GitHub (opens new window)
首页
笔记
个人简历
随笔集
GitHub (opens new window)
  • golang

  • cplus

    • 内存相关

    • 面向对象

      • 相关问题
      • 构造、析构顺序
      • 对象内存布局
      • 静态、动态多态
      • 虚函数表
      • 重载、重写、覆盖
      • 构造函数不能是虚函数
      • CRTP模式的原理和用途
      • 移动语义move
      • 构造函数初始化列表
        • 初始化作用
          • 一、构造函数初始化列表的作用
          • 二、初始化列表的优势
          • 三、什么时候必须使用初始化列表
          • 四、示例对比
          • 五、总结
        • 初始化顺序
          • 一、成员变量的初始化顺序
          • 二、示例说明
          • 三、影响和例子
          • 四、总结
      • 静态成员变量和函数
      • 多重继承
      • 菱形问题与虚继承
      • 对象池
    • STL相关

    • 内置数据结构
    • 数据结构示例
    • go和c++对比
    • 关键字

  • leetcode

  • 存储技术

  • 分布式系统

  • 计算机网络

  • Linux操作系统

  • Redis

  • 其他

  • 笔记
  • cplus
  • 面向对象
lisheng
2024-09-10
目录

构造函数初始化列表

在C++中,构造函数初始化列表是构造函数的一部分,用于在构造对象时初始化类的成员变量。初始化列表的作用和优势以及在什么情况下必须使用它是C++编程中的重要概念。

# 初始化作用

# 一、构造函数初始化列表的作用

构造函数初始化列表的主要作用是初始化类的数据成员,而不是在构造函数体内进行赋值。它可以用于:

  1. 初始化成员变量:直接在对象构造时初始化成员变量的值。
  2. 初始化基类:初始化从基类继承的部分。
  3. 初始化常量成员:对于 const 成员变量,必须在初始化列表中进行初始化,因为常量成员一旦设定值后不能修改。
  4. 初始化引用成员:引用成员必须在初始化列表中初始化,因为引用在对象创建时必须绑定到一个有效的对象上,不能重新赋值。

# 二、初始化列表的优势

  1. 性能:

    • 避免不必要的赋值:初始化列表直接初始化成员变量,而不是先使用默认构造函数创建对象后再赋值。这可以避免两个步骤(默认构造和赋值),特别是对于复杂的数据类型,这样可以提升性能。
    • 直接初始化:例如,对于自定义的类或复杂类型,初始化列表允许直接调用适当的构造函数,从而避免了先初始化再赋值的开销。
  2. 初始化 const 和引用:

    • const 成员变量和引用成员必须在初始化列表中初始化,因为它们的值一旦设定后不能再修改。初始化列表是唯一的合法途径。
  3. 基类初始化:

    • 如果类从一个基类继承,基类的构造函数需要在初始化列表中进行调用。基类构造函数的调用必须在派生类构造函数体执行之前完成。

# 三、什么时候必须使用初始化列表

  1. 初始化 const 成员变量:

    class MyClass {
    public:
        MyClass(int val) : myConstVal(val) {} // 必须在初始化列表中初始化
    private:
        const int myConstVal;
    };
    
    1
    2
    3
    4
    5
    6
  2. 初始化引用成员:

    class MyClass {
    public:
        MyClass(int& ref) : myRef(ref) {} // 必须在初始化列表中初始化
    private:
        int& myRef;
    };
    
    1
    2
    3
    4
    5
    6
  3. 初始化基类:

    class Base {
    public:
        Base(int val) : baseValue(val) {}
    private:
        int baseValue;
    };
    
    class Derived : public Base {
    public:
        Derived(int val) : Base(val) {} // 必须在初始化列表中初始化基类
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  4. 避免不必要的默认构造和赋值:

    class MyClass {
    public:
        MyClass(int val) : myMember(val) {} // 直接在初始化列表中初始化 myMember
    private:
        int myMember;
    };
    
    1
    2
    3
    4
    5
    6

# 四、示例对比

使用初始化列表:

class MyClass {
public:
    MyClass(int val) : myMember(val) {} // 初始化列表直接初始化 myMember
private:
    int myMember;
};
1
2
3
4
5
6

不使用初始化列表:

class MyClass {
public:
    MyClass(int val) {
        myMember = val; // 先默认构造再赋值
    }
private:
    int myMember;
};
1
2
3
4
5
6
7
8

在第二种情况下,myMember 首先会使用默认构造函数初始化(尽管对基本类型来说,这并不显著),然后再赋值。这可能会导致不必要的开销,尤其是在处理复杂类型或自定义类时。

在C++中,类成员变量的初始化顺序是由它们在类定义中的声明顺序决定的,而与它们在构造函数初始化列表中的顺序无关。这一顺序是非常重要的,因为它会影响对象的构造过程及可能的逻辑错误。

# 五、总结

构造函数初始化列表在C++中扮演着重要角色,尤其是当涉及到常量成员、引用成员、基类初始化以及提高性能时。它允许直接在对象创建时初始化成员变量,避免了不必要的赋值操作。正确使用初始化列表不仅可以提高程序的效率,还可以确保 const 和引用成员的正确初始化。在设计类时,应该优先考虑使用初始化列表,尤其是在处理复杂类型和基类时。

# 初始化顺序

# 一、成员变量的初始化顺序

  1. 声明顺序:成员变量的初始化顺序是按照它们在类定义中的声明顺序进行的,而不是初始化列表中的顺序。即使你在初始化列表中列出成员变量的顺序不同,它们仍会按声明顺序被初始化。

  2. 初始化列表顺序:虽然你可以在构造函数的初始化列表中指定任意顺序来初始化成员变量,但实际的初始化顺序依然是按照成员变量在类定义中的声明顺序进行的。

# 二、示例说明

考虑以下示例:

class MyClass {
public:
    MyClass(int a, int b) : memberA(a), memberB(b) {}
    
private:
    int memberA;
    int memberB;
};
1
2
3
4
5
6
7
8

在这个示例中,尽管我们在初始化列表中先初始化 memberA,后初始化 memberB,实际的初始化顺序是 memberA 先于 memberB。这是因为它们在类定义中的声明顺序是 memberA 在前,memberB 在后。

# 三、影响和例子

这种顺序可能会在以下情况下产生影响:

  1. 依赖关系:

    class MyClass {
    public:
        MyClass(int a) : memberB(memberA + a), memberA(a) {}
        
    private:
        int memberA;
        int memberB;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8

    在这个例子中,memberB 的初始化依赖于 memberA 的值。尽管 memberB 在初始化列表中排在 memberA 之后,但 memberA 会在 memberB 之前被初始化,因为它们的初始化顺序取决于声明顺序。因此,上述代码是有效的。

  2. 初始化错误:

    如果你在初始化列表中按照错误的顺序初始化成员变量,并且这些成员之间存在依赖关系,可能会导致逻辑错误或未定义行为。例如:

    class MyClass {
    public:
        MyClass(int a, int b) : memberB(b), memberA(a) {
            // memberA 和 memberB 之间的依赖关系可能导致问题
        }
        
    private:
        int memberA;
        int memberB;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    虽然初始化列表中的顺序是 memberB 先于 memberA,实际的初始化顺序仍然是 memberA 先于 memberB,这可能导致在构造函数体中访问未初始化的 memberB。

# 四、总结

  1. 初始化顺序:类成员变量的初始化顺序是按照它们在类定义中的声明顺序进行的,不受初始化列表中顺序的影响。
  2. 设计考虑:在设计类时,要确保成员变量的初始化顺序合理,以避免未定义行为和逻辑错误。特别是当成员变量之间存在依赖关系时,应确保先初始化那些被其他成员变量依赖的成员变量。
  3. 良好的实践:即使初始化列表中的顺序不影响实际初始化顺序,依然建议在初始化列表中按声明顺序进行初始化,以便代码更具可读性和一致性。
编辑 (opens new window)
上次更新: 2024/09/13, 11:59:12
移动语义move
静态成员变量和函数

← 移动语义move 静态成员变量和函数→

最近更新
01
ceph分布式存储-对象存储(RGW)搭建
10-27
02
ceph分布式存储-集群客户端连接
10-27
03
ceph分布式存储-管理crushmap
10-27
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式