在 C + + 中在哪里声明/定义类作用域常量?

我很好奇 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 文件中进行非整数初始化。

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

只是想听听你的意见,或许还有其他我没考虑过的选择。

107996 次浏览

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

全局名称空间的污染是不好的,因为某些人(例如您使用的库的作者)可能希望将 MyConst用于其他目的。这会导致严重的问题(库不能一起使用等等)

如果常量链接到单个类,那么第二个解决方案显然是最好的。如果这并不容易(想象一下没有绑定到程序中类的物理或数学常量) ,那么名称空间解决方案比这更好。顺便说一句: 如果你必须兼容旧的 C + + 编译器,记住他们中的一些不能在头文件中使用整数初始化-你必须在 C + + 文件中进行初始化,或者在这种情况下使用旧的 enum技巧。

我认为对于常数没有更好的选择-至少现在想不出一个..。

就我个人而言,我使用你的第二种方法; 我已经使用了很多年,它对我很有效。

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

您声称将非整数常量声明为静态类成员“不利于要求在 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)不控制名称的 能见度。他们只控制 无障碍环境。该名称在任何情况下都是可见的。

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

#define read _read

如果您的常数是命名空间污染,那么这就是命名空间核废料。其表现形式是一系列非常奇怪的编译器错误,抱怨丢失了 _ read 函数,但只有在链接到该库时才会出现这种情况。我们最终将 read 函数重命名为其他函数,这并不困难,但应该是不必要的。

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

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

如果只有一个类要使用这些常量,那么在类主体中将它们声明为 static const。如果一组相关的类将要使用这些常量,那么可以在只包含常量和实用程序方法的 class/struct 中声明它们,也可以在专用名称空间中声明它们。比如说,

namespace MyAppAudioConstants
{
//declare constants here
}

如果它们是整个应用程序(或大量应用程序块)使用的常量,则在到处(隐式或显式)包含的标头中的名称空间中声明它们。

namespace MyAppGlobalConstants
{
//declare constants here
}

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

namespace Space
{
const int Pint;
class Class {};
};

但实际上..。

class Class
{
static int Bar() {return 357;}
};