在 C++ 中在哪里声明/定义类范围常量?

Posted

技术标签:

【中文标题】在 C++ 中在哪里声明/定义类范围常量?【英文标题】:Where to declare/define class scope constants in C++? 【发布时间】:2011-01-03 20:23:42 【问题描述】:

我很好奇 C++ 中不同常量声明和定义选项的优缺点。很长一段时间以来,我只是在类定义之前的头文件顶部声明它们:

//.h
const int MyConst = 10;
const string MyStrConst = "String";
class MyClass 
...
;

虽然这会污染全局命名空间(我知道这是一件坏事,但从来没有找到一个坏的原因的清单),常量仍将作用于单个翻译单元,所以文件不包含此标头将无法访问这些常量。但是,如果其他类定义了同名的常量,则可能会发生名称冲突,这可以说不是一件坏事,因为它可能很好地表明了可以重构的区域。

最近,我决定在类定义本身中声明类特定常量会更好:

//.h
class MyClass 
    public:
         static const int MyConst = 10;
...
    private:
         static const string MyStrConst;
...
;
//.cpp
const string MyClass::MyStrConst = "String";

常量的可见性将根据常量是仅在类内部使用还是需要用于使用该类的其他对象进行调整。这是我认为现在最好的选择,主要是因为您可以将内部类常量保持为类私有,并且使用公共常量的任何其他类都会对常量源有更详细的引用(例如 MyClass: :MyConst)。它也不会污染全局命名空间。虽然它确实有需要在 cpp 文件中进行非积分初始化的不利之处。

我还考虑将常量移动到它们自己的头文件中,并将它们包装在命名空间中,以防其他类需要常量,而不是整个类定义。

只是寻找意见和其他可能我还没有考虑过的选项。

【问题讨论】:

【参考方案1】:

您可以在 c++ 文件中将它们声明为全局变量,只要它们不在标头中引用即可。然后它们是该类私有的,不会污染全局命名空间。

【讨论】:

是的,但是在这种情况下,您必须使用 static 或匿名命名空间 - 如果不这样做,在某些实现中,您会污染链接器使用的全局命名空间并在链接时出现名称冲突...【参考方案2】:

全局命名空间的污染很糟糕,因为有人(例如您使用的库的作者)可能想将名称 MyConst 用于其他目的。这可能会导致严重的问题(不能一起使用的库等)

如果常量链接到单个类,您的第二个解决方案显然是最好的。如果这不是那么容易(想想与程序中的类无关的物理或数学常量),命名空间解决方案比这更好。顺便说一句:如果您必须与旧的 C++ 编译器兼容,请记住其中一些不能在头文件中使用积分初始化 - 您必须在 C++ 文件中初始化或在这种情况下使用旧的 enum 技巧。

我认为常量没有更好的选择 - 至少目前想不出...

【讨论】:

【参考方案3】:

我个人使用您的第二种方法;我已经用了很多年了,对我来说效果很好。

从可见性的角度来看,我倾向于使私有常量文件级别静态,因为实现文件之外的任何人都不需要知道它们的存在;如果您需要更改它们的名称或添加新名称,这有助于防止连锁反应重新编译,因为它们的名称范围与其使用范围相同...

【讨论】:

【参考方案4】:

您声称将非整数常量声明为静态类成员“不利于在 cpp 文件中进行非整数初始化”,可以这么说,这并不完全可靠。它确实需要在 cpp 文件中定义,但这不是“损害”,这是您的意图问题。 C++ 中的命名空间级const 对象默认具有内部链接,这意味着在您的原始变体中声明

const string MyStrConst = "String"; 

等价于

static const string MyStrConst = "String"; 

即它将在包含此头文件的每个翻译单元中定义一个独立的MyStrConst 对象。你知道吗?这是不是你的意图?

在任何情况下,如果您特别在每个翻译单元中都不需要一个单独的对象,那么在您的原始示例中声明MyStrConst 常量不是一个好习惯。通常,你只会在头文件中放一个非定义的声明

extern const string MyStrConst; 

并在 cpp 文件中提供定义

const string MyStrConst = "String";

从而确保整个程序使用相同的常量对象。换句话说,当涉及到非整数常量时,通常的做法是在 cpp 文件中定义它们。因此,无论您如何声明它(在类中或在类中),您通常都必须处理必须在 cpp 文件中定义它的“损害”。当然,正如我上面所说,使用命名空间常量,您可以摆脱第一个变体中的内容,但这只是“惰性编码”的一个示例。

无论如何,我认为没有理由使问题过于复杂:如果常量对类有明显的“附件”,则应将其声明为类成员。

附:访问说明符(publicprotectedprivate)不控制名称的可见性。他们只控制它的可访问性。在任何情况下,该名称仍然可见。

【讨论】:

请问可见性和可访问性有什么区别?我认为它们是相同的,你能举个例子吗? @toolchainX: 示例:一个成员函数可能是私有的,但在我们假设定义存在并且编译器可以“看到”它的意义上,该函数可能对编译器是“可见的”。编译器只是强制禁止访问[编译器错误]。删除定义或将定义放在“隐藏”的地方会使这些术语中的函数不“可见”——即使对成员函数也是如此——无论访问如何[链接错误]。 将它放在 .cpp 文件中是不利的,因为编译器无法内联常量,因为它不在编译器的翻译单元中。如果常量只是一些包装整数的类,那么最好将它放在头文件中,以便编译器知道它在所有编译单元中的值。【参考方案5】:

污染全局命名空间应该是不言而喻的。如果我包含一个头文件,我不想遇到或调试该头文件中声明的常量的名称冲突。这些类型的错误非常令人沮丧,有时难以诊断。例如,我曾经不得不链接一个在标题中定义了这个的项目:

#define read _read

如果您的常量是命名空间污染,这就是命名空间核废料。这表现为一系列非常奇怪的编译器错误,抱怨缺少 _read 函数,但仅在链接该库时。我们最终将读取函数重命名为其他内容,这并不难,但应该是不必要的。

您的第二个解决方案非常合理,因为它将变量放入范围。没有理由将它与一个类相关联,如果我需要在类之间共享常量,我将在它们自己的命名空间和头文件中声明常量。这对于编译时不是很好,但有时它是必要的。

我还看到人们将常量放入他们自己的类中,该类可以实现为单例。这对我来说似乎没有回报,该语言为您提供了一些声明常量的工具。

【讨论】:

【参考方案6】:

如果只有一个类要使用这些常量,请在类主体内将它们声明为static const。如果一堆相关的类要使用这些常量,请在仅包含常量和实用程序方法的类/结构中或在专用命名空间中声明它们。例如,

namespace MyAppAudioConstants

     //declare constants here

如果它们是整个应用程序(或大部分应用程序)使用的常量,请将它们声明在标题中的命名空间内,该标题(隐式或显式)包含在任何地方。

namespace MyAppGlobalConstants

    //declare constants here

【讨论】:

【参考方案7】:

不要污染全局命名空间,污染本地。

namespace Space
  
  const int Pint;
  class Class ;
  ;

但实际上...

class Class
  
  static int Bar() return 357;
  ;

【讨论】:

这不清楚。什么是Pint 常量,为什么Bar() 返回375

以上是关于在 C++ 中在哪里声明/定义类范围常量?的主要内容,如果未能解决你的问题,请参考以下文章

C#中在哪里声明全局变量啊,具体位置在哪儿,我是初学者。。。

vb中符号常量的作用范围是啥?

错误:未在此范围内声明“对象”

C++ 错误:未在此范围内声明类/对象

在 JavaScript 中存储的全局范围内创建的常量在哪里?

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量作用域:能够使用变量的代码范围