论cpp中全局变量的特殊性

五月的时候写过一篇讨论关于const内部链接性 的文章,结果稍不留神又犯了一个类似的错误:忽略了C++中对全局常量的特殊处理。

情景如下:在iOS中进行消息传递一般都采用NotificationCenter投递通知的方式进行,而一个Notification的唯一标识就是它的名字。为了减少不同文件间的依赖和耦合,决定把各个消息的名字以全局变量的方式分别定义到相应的.m或者.mm文件中,而不是像以前那样全写到.h内——后者虽然直观,但是有个最大的问题是一旦添加删除消息,就导致所有无关的代码单元重新编译一遍,简而言之:坑爹啊!

做法很简单,比如一个AViewController.m文件需要监听某个通知,而BViewController.m会post这个通知。于是在 AViewController.mm中是:

NSString* const kNotifyA = @”notify_a”; [NotificationCenter defaultCenter] addObserver:self selector:@selector(onNotifyA:) name:kNotifyA object:nil];

而BViewController.m中则:

extern NSString* const kNotifyA; [[NotificationCenter defaultCenter]postNotificationName:kNotifyA object:nil];

在.m中一切OK。但是在.mm文件中却会出现_kNotifyA not referenced之类的错误,链接失败。忙着写代码,于是做了些特殊处理了事。 回家后回顾了下原来那篇文章顺带google了一把,才想明白:C和C++中对于全局常量的处理是有差别的。(.mm就是为了和C++混编特地改的后缀,写objc的童鞋懂的…)如同我那篇文章中所讲extern和const同时使用的时候是会使得变量具有外部连接属性。但是在cpp或者mm文件中定义了的常量变量是具有内部链接属性的(或者说是静态链接,static link),换言之,它对于其他编译单元是不可见的。于是上文中的BViewController.o文件跑去找链接时就找不到kNotifyA这个定义,于是链接失败。

解决方法很简单,大致有三种:

  • 把这种常量定义到.h文件里面,这就是我刚才提到的后一种做法,这样一来各个cpp或者mm文件都有各自的定义。但是问题很明显:其他文件对这个.h的依赖太大。
  • 去掉const 属性,最为推荐,因为本身这个const只是个修饰符,实际项目中没有谁会傻到去改这么一个变量。
  • 定义时加上extern,这个其实原理同2,而且编译器还会报warning,有零warning强迫症的童鞋不推荐。 参考link:http://msdn.microsoft.com/zh-cn/library/0d45ty2d(v=VS.100).aspx

MS的原文: One way to resolve this error is to include the const initializations in a header file and include that header in your CPP files when necessary, just as if it was function prototype. Another possibility is to make the variable non-constant and use a constant reference when assessing it.