转载

关键字之戒

关键字之戒

每次写C函数时,我一般会在其函数名前加上 extern , 或许有人抱有疑问了:C函数本来不就默认处于全局作用域的吗?加不加 extern 都一样。不过如果要限于文件作用域内,倒是该加上 static .

说得好,可是这语法是相当冗余的,即定义一个全局函数有两种形式,一是直接声明二是加上 extern 前缀。如果规则这么简单倒还好,但由于 C/C++ 糟糕的编译模型,我们不得不处理另一种问题: 如何声明一个全局函数?

其实规则应该类似:直接声明一个函数,或在函数的声明加上 extern 前缀, 不定义函数体

然而这意味着代码复杂度两两相乘,即可以有四种情况:

  1. 函数定义没有 extern , 函数声明没有 extern .
  2. 函数定义有 extern , 函数声明没有 extern .
  3. 函数定义没有 extern , 函数声明有 extern .
  4. 函数定义有 extern , 函数声明有 extern .

更何况,您如何确定某文件是不是只有某函数的声明,即函数定义在别的文件。可万一该文件后文还有函数定义呢?再来个 static 语法规则, 记忆负担有您受的。

其实要解决该问题好办,一开始就只记住 externstatic 关键字,且它们分别对应于全局作用域函数和文件作用域函数。若用全局作用域函数,文件要么有 extern 定义,要么有 extern 声明,且有只有一个文件包含 extern 定义,其他文件只能可以有一个 extern 声明;若用文件作用域函数,则在一个文件写 static 定义即可。 禁止用没有 extern 或 static 关键字的函数 ,为了减轻记忆负担,关键字多多益善。

可是,C新手一开始接触到的函数往往是不加 extern 的,如果入门书写的不好,比如依次教「声明与定义」, static , 最后才教 extern . 那新手就很容易被弄得晕头转向,尤其入门书没有教好编译模型的话。事实上 K&R 之所以不适合入门,原因之一就是这个,我记得它没教编译模型,我后来读 Unix 编程环境时,花了好大费劲才弄懂这些。

其实上文关系到了 C/C++ 一个重大的特征之一: 您有时不得通过种种关键字,来保证程序行为预期。 此外,善用关键字,可以大大提高可读性。

我给这现象起了名,就叫「关键字之戒」, 所有合格的 C/C++ 开发者都绕不过这严苛的戒律修行 。掐指一算,我举一些对应的编程规范吧,若无特别说明,一般针对 C++:

  1. 若无意修改函数的引用形参,加 const , 比如复制构造的参数。否则就传普通(引用)形参,比如
  2. 若某类函数不修改数据成员,其函数名后缀加 const .
  3. 定义常量时,别用宏,用 constexpr .
  4. 别让编译器隐式地定义拷贝构造的某函数,自己声明且加 defaultdelete .
  5. 声明接收一个形参的构造函数时,加 explicit , 以免意外的隐式转换构造。
  6. 用 class 时,别省略 publicprivate , 哪怕您知道它默认用 private .
  7. 如果某类会被继承,其析构函数声明为 virtual .
  8. 若某 C 函数不接收任何形参,显式用 void 关键字, 毕竟空形参则意味着形参不定 。
  9. 如果不打算继承类,类名后加 final .

更多规范参见:

不过话说回来,关键字之戒可能恰恰反映了 C/C++ 作为系统编程语言,对底层程序入木三分的控制吧,与复杂度一样名副其实的细度。当然,新一代的系统编程语言应该可以通过更好的语法设计,大破弃键字之戒。比如 C++ 相对于 C, 废除了空形参意味着形参不确定的特性,引入了代替 externstatic 的命名空间,C++17 更有传说中的模块编译模型。Rust 可能更青出于蓝。

Written with StackEdit .

正文到此结束
Loading...