C++中类和命名空间中的静态字符串常量用于常量是什么区别?

25

我想声明字符串常量,这些常量将在项目中的多个类中使用。我考虑了两种选择。

选项1:

#header file 
class constants{
    static const string const1;
};

#cpp file

const string constants::const1="blah";

选项2:

#header file 
namespace constants{
    static const string const1="blah";
};

想知道哪种实现方法更好。

已经看过:

在C ++中存储类特定命名常量的位置

在C ++中放置常量字符串:静态类成员还是匿名命名空间


更新:

选项3:

根据“potatoswatter”和“sellibitze”的建议,我目前有以下实现方式?

#header file
namespace constants{
    extern const string& const1(); //WORKS WITHOUT THE EXTERN  ***WHY***
};

#cpp file
namespace constants{
   const string& const1(){static string* str = new string ("blah"); return *str;}
}

我将头文件包含在需要使用常量的位置。这种实现方式有什么明显的缺点吗?


1
选项2似乎不是一个解决方案。尽管在同一命名空间中出现,但字符串仍将分别为每个源文件定义。 - Potatoswatter
4
如果Shishya能够正确使用Option 2的语法,那么它就是一个解决方案。(没有 "static",字符串只能在头文件中声明,但需要在源文件中定义。) - pkh
@pkh:语法比那稍微棘手一点;v) - Potatoswatter
哎呀,忘了extern关键字。虽然不算“棘手”,但还是有点小问题。 - pkh
1
更新看起来很好。在函数声明中,extern是严格可选的,因此两种方式都可以正常工作。但是你不需要使用new{ static string str( "blah" ); return str; }是通常的方法。 - Potatoswatter
4个回答

14

两年后的更新:

每个可以被多个源文件访问的全局变量都应该用一个inline函数包装起来,这样链接器才能在这些文件之间共享对象,并使程序正确地初始化它。

inline std::string const &const1() {
    static std::string ret = "hello, world!";
    return ret;
}

如果你愿意,inline函数可以被隐式地声明为extern并且可以被放置在一个命名空间或者类中(但是不要仅仅为了存储静态成员而使用类,因为命名空间更适合这个场景。另外不要使用匿名命名空间,否则会破坏链接器,每个源文件将看到不同的std::string对象。)


在存在多个常量的情况下,最好在源文件中使用命名空间常量{...},以使代码更清晰。 - pkh
@Potatoswatter:当您说使用C字符串字面值时,是建议我将std::string替换为char*吗?另外,我该如何避免静态初始化顺序混乱问题? - Asif Mohammed
1
@Shishya:是的。当然,这取决于您是否真正需要std::string。请参阅有关如何避免使用它的页面。您需要创建一个函数来返回全局变量,而不是直接访问全局变量。因此,如果使用std::string的原因是本地化,那么可能会有一个函数std::string const& get_localized_string( int string_index ),它只需返回全局变量即可解决初始化顺序问题。 - Potatoswatter
@Potatoswatter:本项目的本地化不是我的关注点。但我是C++新手,所以我不太清楚切换到char*为什么可以解决SIOF问题。 - Asif Mohammed
@Potataswatter:请就我基于你的建议列出的当前实现版本发表评论。 - Asif Mohammed
显示剩余4条评论

12

所有使用std::string的答案都存在一个风险,即为字符串字面值动态分配内存,而该字面值将在程序(以及二进制文件)的整个生命周期中保持不变,因此应避免使用它们。

sellibitze的答案接近最佳实践,但存在将其声明一次并在其他地方定义的问题,这样做不够优雅且更加繁琐。最好的方法是

namespace constants {
    const char * const blah = "blah!"
    const char * const yada = "yada yada!"
}

这个解决方案在这里进一步讨论。


9
不是这个,我会选择这个:
// header file
namespace constants {
extern const char const1[];
}

// cpp file
namespace constants {
extern const char const1[] = "blah";
}

该头文件包含一个const1的声明,其类型不完整但可转换为char const*。该cpp文件包含具有外部链接的字符数组定义。与std::string不同,没有动态初始化。所以,在我看来这是一个优点。


代码在.cpp文件中不应该是这样的吗?const char constants::const1[] = "blah";,也就是说,没有externnamespace重新声明。 - Remy Lebeau
@Remy:耸肩。你想做什么就做什么吧。但是我发布的代码没有问题。如果你包含了已经声明const1具有外部链接的头文件,那么你可以在cpp文件中删除extern。但是由于在C++中,命名空间范围内的常量变量默认具有内部链接,因此您需要至少一个extern。顺便说一下,我发布的内容也可以在不包含头文件的情况下工作。 - sellibitze

4

选项1以更加混乱的方式达到与选项2相同的效果。

如果你要使用一个只有静态成员的类,尤其是用于全局访问/常量,那么请使用命名空间。


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