C++中的static const

提出问题

以下代码,能编译链接通过吗?

void f(const int &value)
{
}
class Test
{
public:
  static const int a = 1;
};
int main()
{
  f(Test::a);
  return 0;
}

我的第一感觉是:应该没问题,吧。在VS 2013 实验了下,顺利通过,一切正常.然而gcc下是报错的:Undefined reference to ‘Test::a’这是为什么?

分析问题

写作本文时所用的环境是gcc 4.8.2 (Ubuntu 14.04 , X86平台)。注意,本文的讨论只针对类的static const成员,也就是所谓的class scope。namespace scope的情况不属于我们的讨论范围内。

把以上代码保存为test.cpp,然后用gcc编译它:

g++ -c -o test.o test.cpp

这个命令执行之后,我们会在目录下得到test.o文件。接着,我们通过objdump来查看符号表:

objdump -x test.o

我们可以看到类似如下的输出:

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 test.cpp
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .eh_frame  00000000 .eh_frame
00000000 l    d  .comment   00000000 .comment
00000000 g     F .text  00000005 _Z1fRKi
00000005 g     F .text  00000019 main
00000000         *UND*  00000000 _ZN4Test1aE

上面的最后一行,_ZN4Test1aE就是对应我们程序中声明的Test::a。之所以长得这么复杂、奇怪,是因为编译器做了mangling处理。

注意到UND没?根据文档的解释:

UND : if the section is referenced in the file being dumped, but not defined there

也就是_ZN4Test1aE在本.o文件中引用,然而它却木有定义。因此,报Undefined reference to ‘Test::a’的错,也就情理之中了。

那么,我们的程序是否真的引用了_ZN4Test1aE呢?恩,我们接着往下看。

输入如下命令:

g++ -S test.cpp

我们可以得到类似这样的汇编代码(做了整理,节选):

main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    $_ZN4Test1aE, (%esp) ;看到没?_ZN4Test1aE !
    call    _Z1fRKi ;调用函数f
    movl    $0, %eax
    leave
    ret

虽然我们已经分析出为什么会报错,然而,我们还有一个疑问,就是,为什么下面的代码,是OK的?

void f(const int value) //这里没有 &
{
}
class Test
{
public:
  static const int a = 1;
};
int main()
{
  f(Test::a);//没问题
  return 0;
}

恩,有了前面的基础,相信读者诸君已经知道怎么分析。我们可以用同样的方法,看看它的汇编代码:

main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    $1, (%esp)     ;看到没?1,不是_ZN4Test1aE,也不是Test::a
    call    _Z1fi
    movl    $0, %eax
    leave
    ret

也就是说,在这里,编译器只是把Test::a认作一个占位符,实际使用之处用1代替了。

解决问题

知道原因了,那么怎么解决呢?恩,至少三种方法:

1.我们可以定义(而不是声明)Test::a。是的,上面的static const int a = 1;并不是它的定义式。如果要定义,那么我们应该这么做:

void f(const int &value)//还是传引用
{
}
class Test
{
public:
  static const int a;
};
const int Test::a = 1;//定义a
int main()
{
  f(Test::a);//现在没问题了
  return 0;
}

有兴趣的读者可以看看这个程序对应的符号表,就会发现Test::a被放在了程序的rodata section,而不是UND了。

2.如果仅仅声明a,那么我们可以按值的方式使用它,这没问题。也就是只使用它的值;而不去获得它的地址(当然,也包括引用。引用本质上也是地址)。

3.使用枚举类型。是的,枚举!像这样:

void f(const int &value)//还是传引用
{
}
class Test
{
public:
  enum HELLOWORLD {a = 1}; //枚举,而不是 static const
};
int main()
{
  f(Test::a);//没问题
  return 0;
}

那么,这种情况下,编译器是如何处理的呢?就留给读者诸君作为练习吧。

关于程序设计基石与实践更多讨论与交流,敬请关注本博客和新浪微博songzi_tea

时间: 2024-10-20 13:35:08

C++中的static const的相关文章

OC中extern,static,const的用法

1.const的作用: const仅仅用来修饰右边的变量(基本数据变量p,指针变量*p). 例如 NSString *const SIAlertViewWillDismissNotification;修饰的是SIAlertViewWillDismissNotification 被const修饰的变量是只读的 2.static的作用: 修饰局部变量: 1.延长局部变量的生命周期,程序结束才会销毁. 2.局部变量只会生成一份内存,只会初始化一次. 3.改变局部变量的作用域. 修饰全局变量 1.只能在

c中常用的关键字static const volatile

在C语言中,关键字static有三个明显的作用:1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变.2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问.它是一个本地的全局变量.3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用.那就是,这个函数被限制在声明它的模块的本地范围内使用.大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分.这是一个应试者的严重的缺点,因

C++中类中常规变量、const、static、static const(const static)成员变量的声明和初始化

C++类有几种类型的数据成员:普通类型.常量(const).静态(static).静态常量(static const).这里分别探讨以下他们在C++11之前和之后的初始化方式. c++11之前版本的初始化 在C++11之前常规的数据成员变量只能在构造函数里和初始化列表里进行初始化.const类型的成员变量只能在初始化列表里并且必须在这里进行初始化.static类型只能在类外进行初始化.static const类型除了整型数可以在类内初始化,其他的只能在类外初始化.代码如下: class A {

static const readonly

C#中的static 和Java中的static 简单,两者用法完全是一致的.从两方面讨论: 1. 变量是属于类的,不是实例级别的.只能通过类名调用,不能通过实例调用. 2. 如果在定义时就赋值了,那么在类初始化的时候,最先完成所有静态变量的赋值.但是要注意,所有静态变量的初始化顺序是无法确定的. C# 中的const 和Java中的finnal 很长一段时间我一直认为两者是相同的作用,无非是变量初始化后不能更改,即只能在定义时或者构造函数中赋值.然而这仅仅只是片面的,下面将为大家详细分析: 1

static const vs. extern const

在实现文件(.m文件)中使用static const来定义“只在编译单元内可见的常量”(只在.m文件内可见),由于此类常量不在全局符号表中,所以无须为其名称加类名前缀(一般以k开头). 在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值,这种常量会出现在全局符号表中,所以其名称应以类名作前缀,以避免冲突. 参考:http://stackoverflow.com/questions/23652665/static-const-vs-extern-const

C语言 const, static, static const 的区别

基本定义: const  就是只读的意思,只在声明中使用;static 一般有2个作用,规定作用域和存储方式. 对于局部变量, static规定其为静态存储方式, 每次调用的初始值为上一次调用的值,调用结束后存储空间不释放;对于全局变量, 如果以文件划分作用域的话,此变量只在当前文件可见; 对于static函数也是在当前模块内函数可见.static const 应该就是上面两者的合集. PS:1. 全局const,只读的全局变量,其值不可修改.static,规定此全局变量只在当前模块(文件)中可

const和static const的区别

对于C/C++语言来讲,const就是只读的意思,只在声明中使用;static一般有2个作用,规定作用域和存储方式.对于局部变量,static规定其为静态存储方式,每次调用的初始值为上一次调用的值,调用结束后存储空间不释放;对于全局变量,如果以文件划分作用域的话,此变量只在当前文件可见;对于static函数也是在当前模块内函数可见.static const 应该就是上面两者的合集.下面分别说明:全局:const,只读的全局变量,其值不可修改.static,规定此全局变量只在当前模块(文件)中可见

iOS#define和static const

定义常量最好用 static const ,不用#define 编写代码时经常要定义常量.例如,要写一个UI视图类,此视图显示出来之后就播放动画,然后消失.你可能想把播放动画的时间提取为常量.掌握了Objective-C与其C语言基础的人,也许会用这种方法来做: #define ANIMATION_DURATION 0.3 上述预处理指令会把源代码中的ANIMATION_DURATION字符串替换为0.3.这可能就是你想要的效果,不过这样定义出来的常量没有类型信息."持续"(durat

C++类中的static和const

本文列举C++类中的static和const的规则和用法. 以下代码用来举例说明. class A { public: A():m(10) //const成员必须在构造函数的初始化构造列表中初始化 { q = 40; } void fun1()const { m++; //错误.const成员是常量,不能改变其值. n++; //正确.static变量n属于类,但是每个对象的函数都可以访问和改变它. q++; //错误.const成员函数不能改变数据成员的值. } static void fun