构造函数和静态成员:必须显式定义静态成员变量,不能出现在构造的初始化列表中
1 class Fred {
2 public:
3 Fred();
4
5 private:
6 int i_;
7 static int j_;
8 };
Fred::Fred()
: i_(10) // OK: you can (and should) initialize member data this way
, j_(42) // Error: you cannot initialize static member data like this
{
}
// You must define static data members this way:
int Fred::j_ = 42;
通常把静态成员的声明放到.H文件,定义放到.cpp中。如果没有定义,会出现
"undefined external" 的链接错误。
静态变量初始化顺序产生的错误是难以觉察,因为它发生在mian之前,如果你有两个静态成员x,y,分别位于两个文件x.cpp、y.cpp中,而y在初始化要调用x,这样的场景很常见,出错的几率有百分之五十。如果先初始化x,一切OK,如果先初始化y,那就惨了。例如:
1 // File x.cpp
2 #include "Fred.h"
3 Fred x;
4
1 // File y.cpp
2 #include "Barney.h"
3 Barney y;
1 // File Barney.cpp
2 #include "Barney.h"
3
4 Barney::Barney()
5 {
6
7 x.goBowling();
8
9 }
解决这种静态成员初始化的方法很多,一个简单的方法就是用静态方法x()替代Fred x,然后返回这个Fred的引用,如下所示:
1 // File x.cpp
2
3 #include "Fred.h"
4
5 Fred& x()
6 {
7 static Fred* ans = new Fred();
8 return *ans;
9 }
这个静态变量这初始化一次,以后将一直返回同样的Fred对象。这是修改后的代码
1 // File Barney.cpp
2 #include "Barney.h"
3
4 Barney::Barney()
5 {
6
7 x().goBowling();
8
9 }
第一次使用,Fred对象先被构造。但是这个解决方法使用时要慎重,在这里第一选择是使用静态成员,使用静态指针会有一些副作用,倒不是担心内存泄露,在程序退出时系统会自己释放这些堆空间。在使用静态变量时要保证第一次使用前被初始化,最后一次使用后被析构,在这里我们要注意的是析构函数的代码。如果静态变量a、b、c在构造时调用ans没问题,但是在析构时如果还调用ans,程序极有可能崩溃。这种应用在实际中并不多见,解决的方法有三种,待以后的主题中在讲。
在这里有个static initialization和static deinitialization,前者意义大家都知道,后者则是去初始化指的是在应用之前被别的代码给析构了,导致我们用的这个静态量没有初始化,这个是很致命的,尤其在静态指针中,表现的更为明显。
当然static这个关键字也并非一无是处,下面的代码中的错误就可以用staic来解决:
1 #include <iostream>
2
3 int f(); // forward declaration
4 int g(); // forward declaration
5
6 int x = f();
7 int y = g();
8
9 int f()
10 {
11 std::cout << "using 'y' (which is " << y << ")\n";
12 return 3*y + 7;
13 }
14
15 int g()
16 {
17 std::cout << "initializing 'y'\n";
18 return 5;
19 }
这段代码显然不能通过编译,下面通过static改变了初始化的顺序
1 #include <iostream>
2
3 int f(); // forward declaration
4 int g(); // forward declaration
5
6 int x = f();
7 int y = g();
8
9 int f()
10 {
11 std::cout << "using 'y' (which is " << y << ")\n";
12 return 3*y + 7;
13 }
14
15 int g()
16 {
17 std::cout << "initializing 'y'\n";
18 return 5;
19 }
1 #include <iostream>
2
3 int f(); // forward declaration
4 int g(); // forward declaration
5
6 int x = f();
7 int y = g();
8
9 int f()
10 {
11 std::cout << "using 'y' (which is " << y << ")\n";
12 return 3*y + 7;
13 }
14
15 int g()
16 {
17 std::cout << "initializing 'y'\n";
18 return 5;
19 }
但是上面的更改只限于编译器的内置数据类型,而不是用户自定义的数据类型。