配套视频课程已更新完毕,大家可通过以下两种方式观看视频讲解:

关注公众号:爱编程的大丙,或者进入大丙课堂学习。


1. C++98 标准的类成员初始化

在C++98中,支持了在类声明中使用等号 = 加初始值 的方式,来初始化类中静态成员常量 。这种声明方式我们也称之为”就地”声明。而非静态成员变量的初始化则必须在构造函数中进行。

下面通过一段代码来举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
struct Base 
{
Base() : a(250) {}
Base(int num) : a(num) {}

int a;
int b = 1;
static int c = 0;
static const double d = 3.14;
static const char* const e = "i am luffy";
const static int f = 0;
};
如果按照 C++98 标准来解读上面这段代码 ,其中有这么几行语法是错误的:
  • 第7行:类的非静态成员,必须在构造函数中进行初始化

  • 第8行:类的静态成员,必须在类的外部进行初始化

  • 第9行:类的静态常量成员,但不是整形或者枚举,无法通过编译

    如果使用 g++ 可能发现就地初始化 double 类型的静态常量是可以通过编译的,不过这实际是 GNU 对C++的一个扩展,并不遵从C++ 标准。

  • 第10行:类的静态常量成员,但不是整形或者枚举,无法通过编译

  • 第8、9、10行的变量初始化方式是一样的,都是在类的外部

    1
    2
    3
    int Base::c = 110;
    const double Base::d = 3.14;
    const char* const Base::e = "i am luffy";

答疑解惑:上面程序中的 static const 和 const static 是等价的。

2. C++11 标准的类成员初始化

2.1 初始化类的非静态成员

在进行类成员变量初始化的时候,C++11标准对于C++98做了补充,允许在定义类的时候在类内部直接对非静态成员变量进行初始化,在初始化的时候可以使用等号 = 也可以使用花括号 {}

1
2
3
4
5
6
7
8
9
10
11
class Test
{
private:
int a = 9;
int b = {5};
int c{12};
double array[4] = { 3.14, 3.15, 3.16, 3.17};
double array1[4] { 3.14, 3.15, 3.16, 3.17 };
string s1("hello"); // error
string s2{ "hello, world" };
};

可以看到如果使用花括号 {}的方式对类的非静态成员进行初始化,等号是可以省略不写的。

  • 第9行:错误,不能使用小括号() 初始化对象,应该使用花括号{}

2.2 类内部赋值和初始化列表

在C++11之前对于非静态的类成员变量我们除了在构造函数内部进行赋值,也可以在类的初始化列表中进行初始化(这种方式比在构造函数内部赋值效率高)。那么,如果同时在类内部对非静态成员变量就地初始化和在初始化列表中进行初始化会怎么样呢?下面来测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Init
{
public:
Init(int x, int y, int z) :a(x), b(y), c(z) {}

int a = 1;
int b = 2;
int c = 3;
};

int main()
{
Init tmp(10, 20, 30);
cout << "a: " << tmp.a << ", b: " << tmp.b << ", c: " << tmp.c << endl;
return 0;
}
  • 第4行:使用初始化列表对类的非静态成员进行初始化
  • 第6、7、8行:在类内部对非静态成员变量就地初始化(C++11新特性)

执行程序,输出的结果如下:

1
a: 10, b: 20, c: 30

我们可以从函数的打印输出中看到,在类内部就地初始化和初始化列表并不冲突(程序可以正常运行)。程序员可以为同一成员变量既在类内部就地初始化,又在初始化列表中进行初始化,只不过初始化列表总是看起来后作用于非静态成员。也就是说,通过初始化列表指定的值会覆盖就地初始化时指定的值。