关于为什么类的静态成员变量不能立即初始化
2010-08-20 17:03
369 查看
我们都知道代码1是错误的,今天我去追究其原因。当然有些地方属于个人理解,有所纰漏,请不吝惜指正。
// test.h
代码1
当我们写下面的代码时,而没有在cpp文件里给出定义,就会出现无法解析的外部符号错误。这是因为"static int a;"是对变量a的声明。我们都知道,类实例化时,编译器分配内存给成员变量,也就是说,当产生类的实例时定义成员变量,但是对于静态成员变量呢?
代码2
我们假设静态成员变量和其他成员变量没有区别,也是在类实例化时编译器赋予内存,那么如下代码:
代码3
就定义了一个局部对象,那么自然而然的,当这个函数执行完毕的时候,对象t析构,静态成员变量a的空间被释放掉。那就根本无法达到类共享静态成员变量的目的。所以有如下结论:静态成员变量在类实例化之前就已经存在了,并且分配了内存,它交由类专属使用(这是靠static的内部链接属性实现的),类无法控制它何时被分配内存,何时释放。我们不妨把它理解为一个在类中声明,专属于类的全局变量,它的链接属性为内部链接。
谈完静态成员变量的性质以后,让我们来讨论一下为什么静态成员变量不能立即初始化,也就是像代码1那样去做。
假设我们可以将静态成员变量立即初始化,那么代码3展开:
这里面的静态成员变量不再是一个声明,而是定义,当函数fun执行到"t.a = 3;"时,不会再把a当成为一个外部符号,不会把它放入未解决符号表中。a首先被赋值为0,然后被修改为3。再看以下代码:
这里面是同样的道理,"static int a = 0;"不再是一个声明,而是定义,当函数fun执行到"t.a += 3;"时,不会把a当成一个外部符号,不会把它放入未解决符号表中。所以在本编译单元里寻找t.a,发现值为0,那么"t.a += 3;"的执行结果是t.a的值为3,而不是6,没有类的对象共享静态成员变量的目的。
有人可能会问为什么"const static int a = 5;"这样的语句放在类的定义里可以呢,如下面代码:
那是因为const使其成为了常量,不会担心上面所述的问题。但是,这样的定义,虽然值是不会改变,会不会造成无法共享静态成员变量a呢?因为在包含该类的头文件的cpp里面,将#include "test.h"扩展后,都有这么一句"const int a = 5;"这岂不是在不同的地方定义了静态成员变量a么?也就是说,当我们要取a的地址的时候,会不会发现在包含test.h的各个cpp中,取得a的地址不同?
请看下面代码:
该段代码经过反汇编后:
可以看到int b = t.a;的汇编语句是:
00411433 mov dword ptr [b],5
这说明编译器已经对const int static a = 5;优化了。注意:只有静态常量整形数据成员才可以立即初始化。
// test.h
class Test { public: static int a = 5; };
代码1
当我们写下面的代码时,而没有在cpp文件里给出定义,就会出现无法解析的外部符号错误。这是因为"static int a;"是对变量a的声明。我们都知道,类实例化时,编译器分配内存给成员变量,也就是说,当产生类的实例时定义成员变量,但是对于静态成员变量呢?
// test.h class Test { public: static int a; };
代码2
我们假设静态成员变量和其他成员变量没有区别,也是在类实例化时编译器赋予内存,那么如下代码:
// farm1.cpp #include "test.h" void fun() { Test t; t.a = 3; }
代码3
就定义了一个局部对象,那么自然而然的,当这个函数执行完毕的时候,对象t析构,静态成员变量a的空间被释放掉。那就根本无法达到类共享静态成员变量的目的。所以有如下结论:静态成员变量在类实例化之前就已经存在了,并且分配了内存,它交由类专属使用(这是靠static的内部链接属性实现的),类无法控制它何时被分配内存,何时释放。我们不妨把它理解为一个在类中声明,专属于类的全局变量,它的链接属性为内部链接。
谈完静态成员变量的性质以后,让我们来讨论一下为什么静态成员变量不能立即初始化,也就是像代码1那样去做。
假设我们可以将静态成员变量立即初始化,那么代码3展开:
// farm1.cpp class Test { public: static int a = 0; } void fun() { Test t; t.a = 3; }
这里面的静态成员变量不再是一个声明,而是定义,当函数fun执行到"t.a = 3;"时,不会再把a当成为一个外部符号,不会把它放入未解决符号表中。a首先被赋值为0,然后被修改为3。再看以下代码:
// farm2.cpp #include "test.h" void fun() { Test t; t.a += 3; } 展开后: // farm2.cpp class Test { public: static int a = 0; }; void fun() { Test t; t.a += 3; }
这里面是同样的道理,"static int a = 0;"不再是一个声明,而是定义,当函数fun执行到"t.a += 3;"时,不会把a当成一个外部符号,不会把它放入未解决符号表中。所以在本编译单元里寻找t.a,发现值为0,那么"t.a += 3;"的执行结果是t.a的值为3,而不是6,没有类的对象共享静态成员变量的目的。
有人可能会问为什么"const static int a = 5;"这样的语句放在类的定义里可以呢,如下面代码:
// test.h class Test { public: const static int a = 5; }
那是因为const使其成为了常量,不会担心上面所述的问题。但是,这样的定义,虽然值是不会改变,会不会造成无法共享静态成员变量a呢?因为在包含该类的头文件的cpp里面,将#include "test.h"扩展后,都有这么一句"const int a = 5;"这岂不是在不同的地方定义了静态成员变量a么?也就是说,当我们要取a的地址的时候,会不会发现在包含test.h的各个cpp中,取得a的地址不同?
请看下面代码:
#include "test.h" void Farm1::test() { Test t; int b = t.a; }
该段代码经过反汇编后:
void Farm2::test() { 00411410 push ebp 00411411 mov ebp,esp 00411413 sub esp,0E8h 00411419 push ebx 0041141A push esi 0041141B push edi 0041141C push ecx 0041141D lea edi,[ebp-0E8h] 00411423 mov ecx,3Ah 00411428 mov eax,0CCCCCCCCh 0041142D rep stos dword ptr es:[edi] 0041142F pop ecx 00411430 mov dword ptr [ebp-8],ecx Test t; int b = t.a; 00411433 mov dword ptr [b],5 }
可以看到int b = t.a;的汇编语句是:
00411433 mov dword ptr [b],5
这说明编译器已经对const int static a = 5;优化了。注意:只有静态常量整形数据成员才可以立即初始化。
相关文章推荐
- 关于为什么类的静态成员变量不能立即初始化
- 关于为什么类的静态成员变量不能立即初始化
- 关于为什么类的静态成员变量不能立即初始化
- 关于C++ 类数据成员初始化的一点总结【为什么类定义中不能初始化成员变量】
- 关于c++ 的静态成员变量为什么一定要初始化
- 关于Windows编程(MFC、API等 自己明白就可)向窗口写内容不能立即显示的问题
- 关于为什么不能在vc6.0中引入cpp文件
- 【c++】为什么类中静态(static)成员不能在类的定义内初始化?
- 静态成员变量初始化问题。关于 error LNK2005 static int:已经在***.obj中定义
- 关于01背包问题优化的时候为什么不能正向跑
- 关于虚拟机为什么不能用bridge模式上网
- 关于Java为什么配置好环境变量但是不能在命令行cmd运行javac的问题
- 为什么static成员必须在类外初始化,而不能在类的头文件中初始化
- 关于为什么不能在头文件中包含变量定义的解释
- 为什么c++ 中类内初始值不能使用圆括号初始化?
- 为什么临时对象(变量)不能初始化非const引用对象(变量)
- 关于原型链和继承问题的思考:为什么不能直接把父类的prototype赋值给子类的prototype
- 关于C/C++中switch语句case中变量不能初始化赋值的问题
- 关于java重载为什么不能用不同返回值区分