在命名空间中,extern关键字是如何工作的?

11

我在运行一个类似于我在这里找到的简单程序。它旨在在多个文件中包含常量时减少代码膨胀。它通过在命名空间内使用const全局变量及其相应的extern前向声明来实现。

globals.h

#ifndef GLOBALS_H_
#define GLOBALS_H_

namespace Constants
{
    // forward declarations only
    extern const double pi;
    extern const double avogadro;
    extern const double my_gravity;
}

#endif

globals.cpp

namespace Constants
{
    // actual global variables
    extern const double pi(3.14159);
    extern const double avogadro(6.0221413e23);
    extern const double my_gravity(9.2); // m/s^2 -- gravity is light on this planet
}

source.cpp

#include <iostream>
#include <limits>

#include "globals.h"

int main()
{
    double value_of_pi = Constants::pi;

    std::cout << value_of_pi;

    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.get();

    return 0;
}

我假设 Constants::pi 获取了包含在 globals.cpp 的 Constants 命名空间中的 pi 值,并且能够这样做,因为它可以从已包含的 globals.h 中访问该命名空间。但我不理解为什么 globals.cpp 中的 const 全局定义/初始化 需要使用 extern 关键字?我尝试删除 globals.cpp 中的 extern 关键字,认为它不是必需的,但是我的程序无法正常运行。我认为 extern 只用于前向声明,那么为什么在const全局定义/初始化中需要它?是否与它们所定义的命名空间有关?

1个回答

15

这是为了减少在多个文件中包含常量时的代码膨胀

我建议不要专注于这种优化,除非它真的必要,而应选择最简单的设计:直接在头文件中定义这些常量,并从需要访问这些常量的所有翻译单元 (".cpp 文件") 中包含该头文件。由于这些对象是const,因此它们将具有内部链接,链接器不会因违反一个定义规则而呼啸。

我尝试删除globals.cpp中的extern关键字,认为它不需要,但没有它我的程序就无法运行

那是因为具有静态存储期限的命名空间范围const对象(例如您的pi变量)具有内部链接,除非您明确将它们定义为extern

我以为extern仅用于前向声明?

extern用于声明在另一个翻译单元 (".cpp 文件") 中定义的变量。如果对象是const,则定义它的翻译单元需要明确将其标记为extern,以便它具有外部链接并且可以从其他翻译单元中看到(如果对象不是const,则不需要这样做)。

这与它们定义的命名空间有关吗?

不,这是所有命名空间级别具有静态存储期限的const对象的规则,并且在C ++标准的第[基本链接] / 3段中指定:

具有命名空间范围 (3.3.6) 的名称如果是

(3.1) [...] — 明确声明为静态的变量、函数或函数模板;或者

(3.2) — 非易失性const限定类型的变量既未明确声明为外部引用也没有先前声明为具有外部链接;或

(3.3) — 匿名联合体的数据成员。


2
我感觉有点被迫要点踩,因为你没有指出他的场景是多么愚蠢,而只关注于语言方面的问题,这对提问者的帮助有限。 - Puppy
1
@Puppy:请放心点踩。你的观点是有道理的。不过要记住,它们不是“可变变量”,正如你在评论问题时所写的那样。它们是const对象。但没关系,我会进行编辑。 - Andy Prowl
这些不是,但他所取代码页面上的其他例子是。 - Puppy
女士们,我在生命中可能只会使用全局变量一两次。 我只是想要了解这种语言,所以如果我遇到写得不好的代码,我会知道它的含义。@AndyProwl 谢谢您详细的解释。现在我大部分都明白了,只有两件事还不清楚。从我提取此内容的网站上,它说 Constants 命名空间内的变量是真正的全局变量,但我认为命名空间具有作用域,因此其中的任何内容都不可以具备全局范围?并且,Constants::pi是否从 globals.h 中 pi 的前向声明中获取标识符 - Wandering Fool
2
@WanderingFool:它们是命名空间级别的,通常被称为“全局”,因为它们不在块作用域中,但这也不完全准确,因为它们不是全局命名空间的一部分。它们具有静态存储期,在进入main()之前创建并在离开后销毁,但它们确实存在于Constants命名空间中,为了访问它们的名称,您必须指定Constants::(或通过使用指令或使用声明隐式地发生)。我想最后一个问题的答案是“是”:由于头文件的原因,名称是可见的。 - Andy Prowl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接