【问题标题】:Why should I use the extern keyword to declare variables in a namespace scope?为什么要使用 extern 关键字在命名空间范围内声明变量?
【发布时间】:2019-05-28 12:51:03
【问题描述】:

我对 C++ 还是很陌生,目前正在学习它的短期课程。我有一些Java背景。

我希望有一个名为“Message”的命名空间,它将用于存储不变/恒定的字符串,这些字符串将在我的程序中的各种不同类中使用。 (例如标题、关键字、名称等)。

如果所有这些字符串都在一个类中,它们将是 const 和静态的,因此我觉得最好将它们放入命名空间而不是类。我当前的“Message.h”看起来有点像这样:

#ifndef MESSAGE
#define MESSAGE

#include <string>

namespace Message {
    const std::string NAME = "Car";
    const std::string SEPARATE = " | ";
    const std::string COMMAND = "Please enter a 1, 2 or a 3: ";
};

#endif MESSAGE

直到教练建议我把它改成这样……

消息.h:

#ifndef MESSAGE
#define MESSAGE

#include <string>

namespace Message {
    extern const std::string NAME;
    extern const std::string SEPARATE;
    extern const std::string COMMAND;
};

#endif MESSAGE

Message.cpp:

#include "Message.h"

const std::string Message::NAME = "Car";
const std::string Message::SEPARATE = " | ";
const std::string Message::COMMAND = "Please enter a 1, 2 or a 3: ";

在课程结束之前,我几乎没有时间从导师那里得到澄清,而且我还需要一段时间才能有机会澄清。根据我的研究,它与翻译单元有关,更具体地说,是尝试在不同的翻译单元中使用变量。

我了解这个的一般概念,但我不太明白在这种情况下使用 extern 的好处?

这里的包含守卫是否足以让 Message:: 命名空间变量不会被多次声明/定义?为什么在这种情况下推荐使用 extern 关键字,这纯粹是为了提高编译速度?

【问题讨论】:

  • “好处”是当使用多个翻译单元时,最终结果是有效的 C++,而不是链接失败、未定义的行为和/或违反 One Definition Ruleextern 关键字不是“推荐的”。这是必需的。
  • 老实说,您可以自己轻松尝试。当您将Message.h 包含在多个文件中、构建并将它们链接在一起时会发生什么?
  • 我建议您详细了解声明定义之间的区别。您可以拥有任意数量的符号声明(当然,如果它们都相同的话),但只有一个定义。
  • @SamVarshavchik So... 在第一种使用头文件的情况下,它声明并定义了每次包含 Message.h 的变量,从而打破了单一定义规则。唯一允许它编译的是包含守卫?该程序在两种情况下都成功编译并按预期运行(VS 2017)。
  • @Jaymaican -- 看起来人们忽略了每个字符串对象上的const。这些对象具有内部链接(即,它们在包含该标题的任何翻译单元之外不可见),因此不存在 ODR 违规。

标签: c++ namespaces constants definition extern


【解决方案1】:

两者都可以正常工作(尽管问题已经得到了下意识的cmets)。区别有点微妙。

在第一个示例中,包含该标题的每个翻译单元(想想 .cpp 文件)都将获得这三个字符串中的每一个的副本。可以这样做,因为它们被标记为const,所以它们不会从翻译单元中导出。如果它们不是const,那么您将拥有同一个符号的多个定义。

在第二个示例中,三个字符串中的每一个都只有一个副本。这些副本位于 Message.cpp 中,并将链接到您的可执行文件中。任何包含 Message.h 的翻译单元都会知道这些名称,因为它们在标头中声明,并且可以使用它们。

对于像常量int 值这样的小东西,第一种方法是最常见的。对于较大的事物,例如字符串对象(通常需要非平凡初始化的事物),第二种方法是最常见的。

【讨论】:

  • 非常感谢!这对我来说很有意义。我正在学习的课程的一个重点是编写符合专业标准的 C++ 代码。这可能是一个有点模糊的问题,但是以第一种方式声明和定义常量 int 值会被认为不那么专业/标准吗?
  • @Jaymaican -- 好的,进入兔子洞。 如果你不获取const int 值的地址,并且它被初始化为一个固定值,编译器可以在任何需要的地方使用该值,而无需将该值作为命名变量存储在任何地方。这是一个很常见的优化,也是使用第一种方法的主要原因。
  • @Jaymaican -- 完全正确。这是“好像”规则的一个示例:如果您的代码无法检测到它执行了该对象,编译器可以忽略该对象。
  • 再次感谢! (道歉,但我在这里的术语有点短)不采用 const int 值的地址是什么意思?这与不创建指向它的指针/引用有关吗? (对于任何阅读此内容的人,我不小心删除了此评论。)
猜你喜欢
  • 2014-08-11
  • 1970-01-01
  • 2011-08-27
  • 2018-03-15
  • 2022-07-06
  • 2021-11-09
  • 2011-01-14
相关资源
最近更新 更多