5

在 C++ 中,如果我想定义一些可以在不同的类、函数、文件中使用的非本地 const 字符串,我知道的方法是:

  1. 使用定义指令,例如

    #define STR_VALUE "some_string_value"
    
  2. const 类成员变量,例如

    class Demo {  
    public:  
      static const std::string ConstStrVal;  
    };  
    // then in cpp  
    std::string Demo::ConstStrVal = "some_string_value";  
    
  3. const 类成员函数,例如

    class Demo{  
    public:  
      static const std::string GetValue(){return "some_string_value";}  
    };  
    

现在我不清楚的是,如果我们使用第二种方法,变量 ConstStrVal 是否总是在任何代码实际使用之前初始化为“some_string_value”?由于“静态初始化顺序惨败”,我对此感到担忧。如果这个问题是有效的,为什么每个人都使用第二种方法?

哪个是最好的方法,2 还是 3?我知道#define 指令不尊重范围,大多数人不推荐它。

谢谢!

4

4 回答 4

5

如果我们使用第二种方法,变量 ConstStrVal 是否总是在任何情况下被任何代码实际使用之前初始化为“some_string_value”?

这取决于它被初始化的值,以及初始化的顺序。ConstStrVal有一个全局构造函数。

考虑使用构造函数添加另一个全局对象:

static const std::string ConstStrVal2(ConstStrVal);

顺序不是由语言定义的,并且ConstStrVal2可以在构造之前调用 的ConstStrVal构造函数。

初始化顺序可能因多种原因而有所不同,但通常由您的工具链指定。更改链接对象文件的顺序可能(例如)更改图像初始化的顺序,然后错误就会浮出水面。

为什么每个人都使用第二种方法?

许多人出于很好的理由使用其他方法……</p>

哪个是最好的方法,2 还是 3?

3号。你也可以避免像这样的多重结构:

class Demo {
public:  
  static const std::string& GetValue() {
    // this is constructed exactly once, when the function is first called
    static const std::string s("some_string_value");
    return s;
  }  
};  

警告:这种方法仍然能够解决中看到的初始化问题ConstStrVal2(ConstStrVal)。但是,与具有全局构造函数的对象相比,您可以更好地控制初始化顺序,并且更容易解决可移植的问题。

于 2012-04-21T06:07:32.290 回答
1

一般来说,我(和许多其他人)更喜欢使用函数来返回值而不是变量,因为函数为将来的增强提供了更大的灵活性。请记住,在成功的软件项目上花费的大部分时间是维护和增强代码,而不是一开始就编写代码。很难预测您今天的常量是否可能不是明天的编译时间常量。也许有一天它会从配置文件中读取。

所以我推荐方法 3,因为它可以满足您今天的需求,并为未来留下更多的灵活性。

于 2012-04-21T05:54:20.813 回答
0

避免将预处理器与 C++ 一起使用。另外,为什么你会在一个类中有一个字符串,但在其他类中需要它?我会重新评估你的类设计以允许更好的封装。如果你绝对需要这个全局字符串,那么我会考虑添加一个 globals.h/cpp 模块,然后在那里声明/定义字符串:

const char* const kMyErrorMsg = "This is my error message!";
于 2012-04-21T06:03:42.983 回答
0

不要在 C++ 中使用预处理器指令,除非您试图实现任何其他方式都无法实现的神圣目的。

从标准(3.6.2):

具有静态存储持续时间(3.7.1)的对象应在任何其他初始化发生之前进行零初始化(8.5)。具有静态存储持续时间的引用和具有静态存储持续时间的 POD 类型的对象可以使用常量表达式(5.19)进行初始化;这称为常量初始化。零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。对象的动态初始化是有序的或无序的。显式专门化的类模板静态数据成员的定义已排序初始化。其他类模板静态数据成员(即,隐式或显式实例化的特化)具有无序初始化。命名空间范围内定义的其他对象已按顺序初始化。在单个翻译单元中定义并具有有序初始化的对象应按照它们在翻译单元中的定义顺序进行初始化。对于具有无序初始化的对象和在不同翻译单元中定义的对象,初始化的顺序是未指定的。

因此,2 的命运取决于您的变量是静态初始化的还是动态初始化的。例如,在您的具体示例中,如果您使用const char * Demo::ConstStrVal = "some_string_value";const char Demo::ConstStrVal[]如果该值在程序中保持不变,则更好),您可以确保无论如何它都会被初始化。使用 a std::string,您无法确定,因为它不是 POD 类型(我不确定这个,但相当肯定)。

第三种方法可以让您确定,贾斯汀回答中的方法确保没有不必要的结构。尽管请记住,静态方法有一个隐藏的开销,即检查变量是否已经在每次调用时初始化。如果您要返回一个简单的常量,那么仅返回您的值肯定会更快,因为该函数可能会被内联。

综上所述,尝试编写程序以免依赖静态初始化。静态变量最好被认为是一种方便,当您不得不处理它们的初始化顺序时,它们不再方便了。

于 2012-04-21T06:23:12.333 回答