论 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.