本文共 2106 字,大约阅读时间需要 7 分钟。
在C++编程中,成员变量的初始化顺序一直是许多开发者的头痛所在。特别是面试中,一个看似简单的问题可能会让人一筹莫展。记得《剑指offer》中提到的那个面试案例?对方声称精通C++却不清楚成员变量初始化顺序,最终回答错误。这背后隐藏着很多技术深度,也让我自己不得不反思。
首先,成员变量的初始化顺序与它们在构造函数中的初始化列表顺序无关。很多开发者误以为初始化列表的顺序决定了变量的初始化顺序,但实际上这与定义成员变量的顺序有关。
成员变量的初始化顺序是基于它们在内存中的排列顺序决定的。内存是按照编译期确定的顺序排列的,因此在类定义中的成员变量定义顺序决定了它们在内存中的存放顺序。因此,初始化列表中的变量顺序并不影响它们的初始化顺序。
来看一个典型的例子:
class A {private: int n1; int n2;public: A(): n2(0), n1(n2 + 2) {} void Print() { cout << "n1:" << n1 << ", n2: " << n2 << endl; }};
在这个构造函数中,n2被初始化为0,然后n1被初始化为n2 + 2。这意味着n2已经被初始化完毕,可以安全地被n1引用。
然而,许多开发者会误以为n1的初始化会被执行在n2之前,这显然与实际情况不同。这是因为成员变量的初始化顺序是由它们在内存中的排列决定的,而不是构造函数的初始化列表顺序。
正确的理解是,成员变量的初始化顺序与它们在内存中的排列顺序一致。内存排列顺序由成员变量在类定义中的定义顺序决定。
因此,在构造函数的初始化列表中,成员变量的顺序并不影响它们的初始化顺序。它们总是按照它们在内存中的排列顺序被初始化的。
很多开发者误以为在构造函数中手动初始化成员变量会影响它们的初始化顺序。实际上,只有在构造函数中手动初始化时,成员变量的初始化顺序才会与它们在构造函数中的定义顺序一致。
例如:
class A {private: int n1; int n2;public: A() { n2 = 0; n1 = n2 + 2; } void Print() { cout << "n1:" << n1 << ", n2: " << n2 << endl; }};
在这个构造函数中,n2被手动初始化为0,然后n1被初始化为n2 + 2。因此,n2必须先被初始化,否则n1的初始化会失败。
需要注意的是,const成员常量必须在构造函数的初始化列表中初始化。不能在构造函数体内进行初始化。
此外,static成员变量必须在类外初始化。它们不能在类内进行初始化。
对于静态变量,初始化顺序遵循以下规则:
需要注意的是,static变量和全局变量都被存放在公共内存区。因此,static变量可以被理解为带有“作用域”的全局变量。
在所有静态变量都被初始化后,main函数才会被调用。如果某个类的构造函数被执行,基类的成员变量会被初始化。
通过以上分析,可以得出以下结论:
以下是正确的构造函数示例:
class A {private: int n1; int n2; const int n3 = 42;public: A() : n2(0), n1(n2 + 2), n3(10) {} void Print() { cout << "n1:" << n1 << ", n2: " << n2 << ", n3: " << n3 << endl; }};
在这个构造函数中:
由于n3是常量,只能在初始化列表中初始化。
通过以上分析,我们可以清晰地看到成员变量初始化顺序的关键点。理解这些规则可以帮助我们避免常见的错误,并提高代码的正确性。记住,成员变量的初始化顺序是由它们在内存中的排列顺序决定的,而不是构造函数中的初始化列表顺序。
转载地址:http://brrfk.baihongyu.com/