C++类内初始化

Share on:
300 Words | Read in about 2 Min | View times

Overview

现代C++提供了3种对类成员的初始化方式,即构造函数初始化、初始化列表,以及C++11新加入的类内初始化。本节内容将具体讲解这3种类成员初始化方式的用法,并比较他们之间的区别。

本系列文章将包括以下领域:

本章其他内容请见 《现代C++》

我们之前在《C++ extern关键字》这篇文章中提到过声明和定义的概念,我们再复习一下这些知识。

声明是用于向程序表明变量的类型和名字,不会实际分配内存空间;定义则会为变量分配空间,但是未必会给分配的内存空间的变量赋予初始值。定义的范围不止是变量,还包含函数等其他内容。

初始化是会为已经定义的变量赋予初始值。初始化只针对变量,而定义可以包括变量,也可以包括函数,且定义的变量可以不初始化。

传统C++对类成员的初始化方式有构造函数初始化以及初始化列表,现代C++加入了类内初始化的方式。

类内成员初始化的方式

构造函数初始化

在构造函数的函数体内对类的成员变量进行初始化:

1class A {
2public:
3    int m_a;
4    A() { m_a = 0; }
5    A(int a) { m_a = a; }
6};

初始化列表

在构造函数名后面紧跟冒号,冒号后面是类的成员变量,再接圆括号,其中是这个成员变量的初始值。这样的结构就是构造函数的初始化列表:

1class A {
2public:
3    int m_a;
4    A() : m_a(0) { }
5    A(int a) : m_a(a) { }
6};

类内初始化

对于初始化列表的方式,如果类内有多个构造函数,那么每个函数都要单独维护一个初始化列表,非常容易造成遗漏的状况出现,而且代码会显得非常冗长。因此C++11引入了类内初始化,可以进行极大简化。类内初始化是在定义类成员变量的时候直接对其赋值,赋值的方式可以使用等号赋值,也可以使用大括号赋值:

1class A {
2public:
3    //...
4    int m_a = 0;
5    int m_b{0};
6};

有同学会疑问,这有啥新奇的,传统C++不就已经可以在类内声明成员变量的时候使用等号来初始化了吗?

是的,但是作用范围非常有限。

传统C++在类内声明成员变量时进行赋值,只能针对类中的静态常量成员,即声明为static const的成员变量,单独的const成员和static成员都不能直接初始化。而且这些静态常量成员变量,也只能是整型或者枚举类型才可以进行初始化。非静态常量的其他成员变量只能在构造函数里进行初始化。

 1//C++98
 2class A {
 3public:
 4    //...
 5
 6    static const int m_a = 0; //ok
 7
 8    int m_b = 0; //invalid
 9    static int m_c = 0; //invalid
10    const int m_d = 0; //invalid
11
12    static const double m_e = 1.3; //invalid
13    static const char* m_f = "hello"; //invalid
14};

初始化方式优先级

在现代C++中,三种类内成员初始化方式都可以共同存在,这个时候可能会出现对同一个成员变量使用了多种方式进行初始化,那么它们之间的优先级如何呢?

初始化的顺序是先类内初始化,再初始化列表,最后构造函数初始化,后面的初始化会把前面的覆盖掉。

 1class A {
 2public:
 3    int m_a = 0;
 4    A(int a) : m_a(1) { m_a = a; }
 5};
 6
 7int main() {
 8    A a(3);
 9    std::cout << a.m_a << std::endl; //3
10    return 0;
11}

初始化方式的适用场景

构造函数初始化适用场景

  • 适用于大多数场景

  • 用于构造函数和拷贝构造函数

  • 用于将多个成员变量都初始化为同一个值:

1class A {
2public:
3    A() { m_a = m_b = m_c = 0; }
4private:
5    int m_a;
6    int m_b;
7    int m_c;
8};

初始化列表的适用场景

  • const成员变量,只能用初始化列表完成初始化,而且不能在构造函数中赋值

  • 成员变量是引用类型的成员变量,只能用初始化列表完成初始化,而且不能在构造函数中赋值

  • 成员变量是自定义类型的对象,建议使用初始化列表初始化

因为构造函数初始化的本质是赋值操作,赋值过程会产生临时对象,临时对象构造和析构都有开销,效率不高,所以推荐使用初始化列表。

类内初始化的适用场景

  • 查看代码定义时更加直观看到初始值

  • 定义多个构造函数时不用对每个构造函数都写一遍初始化列表或构造函数初始化

Prev Post: 『C++闭包』
Next Post: 『C++原始字符串字面量』