让我们考虑一个 C++ 类。在执行开始时,我想从 XML 文件中读取一组值并将它们分配给此类的 7 个数据成员。这些值在整个执行过程中不会改变,它们必须由相关类的所有对象/实例共享。静态数据成员是实现此行为的最优雅方式吗?(当然,我不考虑全局变量)
8 回答
正如其他人所提到的,在这种情况下使用静态成员似乎是合适的。请记住,这不是万无一失的;全局数据的一些问题适用于静态成员:
- 您无法控制静态成员的初始化顺序,因此您需要确保没有全局变量或其他静态变量引用这些对象。有关更多详细信息以及避免此问题的一些提示,请参阅此 C++ 常见问题解答。
- 如果您在多线程环境中访问这些,则需要确保在生成任何线程之前成员已完全初始化。
听起来对我来说很好地使用了静态变量。您更多地将这些用作固定参数而不是变量,并且合法地需要共享这些值。
静态成员可以在这里工作并且完全可以接受。另一种选择是对包含这些成员的类使用单例模式,以确保它们只被构造/设置一次。
听起来像是对静态类成员的适当使用。只是不要忘记它们实际上是具有命名空间和(也许)一些保护的全局变量。因此,如果您的应用程序有朝一日可能会演变出单独的“环境”或需要为每个环境设置一组这些全局变量的东西,那么您就会把自己画到一个角落里。
正如 Rob 所建议的,考虑使用单例,这在以后更容易变成某种托管环境变量。
在执行开始时,我想从 XML 文件中读取一组值并将它们分配给此类的 7 个数据成员。这些值在整个执行过程中不会改变,它们必须由相关类的所有对象/实例共享。
粗体字是这里的关键。只要该语句成立,就可以使用静态变量。这将如何执行?
很难。因此,如果您现在使用该声明始终正确,请继续。如果您希望将来的某个开发人员(或您)使用您的类错误(例如在程序中途读取另一个 XML 文件),那么请执行 Rasmus Farber 所说的类似操作。
是的,静态数据成员就是您要寻找的。但是您必须注意静态变量的初始化/销毁顺序。C++ 中没有机制可以确保在跨翻译单元使用静态变量之前对其进行初始化。为了安全起见,请使用看起来像单例模式并且众所周知可以解决该问题的模式。它之所以有效,是因为:
- 所有静态对象都是在任何 xml_stuff 实例完整构造之后完整构造的。
- C++ 中静态对象的销毁顺序与它们的构造完成(当它们的构造函数完成执行时)完全相反。
代码:
class xml_stuff {
public:
xml_stuff() {
// 1. touch all members once
// => 2. they are created before used
// => 3. they are created before the first xml_stuff instance
// => 4. they will be destructed after the last xml_stuff instance is
// destructed at program exit.
get_member1();
get_member2();
get_member3();
// ...
}
// the first time their respective function is called, these
// objects will be created and references to them are returned.
static type1 & get_member1() { static type1 t; return t; }
static type2 & get_member2() { static type2 t; return t; }
static type1 & get_member3() { static type1 t; return t; }
// ... all other 7 members
};
现在, xml_stuff::get_memberN() 返回的对象在任何 xml_stuff 实例的整个生命周期内都是有效的,因为这些成员中的任何一个都是在任何 xml_stuff 实例之前构造的。使用纯静态数据成员,您无法确保这一点,因为跨翻译单元的创建顺序在 C++ 中未定义。
只要您考虑可测试性,并且除了读取文件之外,您还有另一种设置静态变量的方法,而且您不依赖于整个过程执行时间不变的数据 - 您应该没问题。
我发现在设计代码时考虑编写测试有助于保持代码的良好分解和可重用性。