在 C++ 中,我们有两个全局对象,它们都由不同文件中的其他对象定义。一个对象的构建依赖于已经构建的另一个对象。
class independent;
class dependent;
independent o1;
dependent o2;
为了确保 o1 在 o2 之前构造,我可以通过在 o2 之前声明 o1 来做到这一点。
这可以确保 o1 是在 o2 之前构建的吗?如果编译器更改了 odering 怎么办?
谢谢
在 C++ 中,我们有两个全局对象,它们都由不同文件中的其他对象定义。一个对象的构建依赖于已经构建的另一个对象。
class independent;
class dependent;
independent o1;
dependent o2;
为了确保 o1 在 o2 之前构造,我可以通过在 o2 之前声明 o1 来做到这一点。
这可以确保 o1 是在 o2 之前构建的吗?如果编译器更改了 odering 怎么办?
谢谢
A
依赖B
则A
应该B
在其构造函数中作为参数。C++ 标准保证在同一编译单元中按顺序初始化静态变量,但在单独的编译单元中未定义顺序。这通常被称为“静态初始化顺序惨败”。
因此,如果您的变量在同一个 .cpp 中声明,它们会按照您声明它们的顺序进行初始化,如果它们位于单独的文件中,您根本不知道(我见过非常激进的链接优化的案例)无论情况如何,都会改变静态变量初始化的顺序,但这不符合标准,并且不应该在正常用例中发生)。
你的问题背后是一个设计问题。
如果你的一个类依赖于另一个类,那么它的结构应该明确地显示这种依赖关系,并且不可能出现依赖关系不满足的情况。
如果您有一个全局对象依赖于您无法明确控制的另一个全局对象,那么依赖对象可能不应该是全局对象。
您的问题没有提供足够的详细信息来了解最适合您的设计,因此这里有一些建议,您可以从中做出决定。
所以首先,让我们表达这种依赖关系......
类的实例dependent
需要一个independent
用于初始化的对象,但以后不再需要它。然后只需使默认构造函数不可访问并定义您自己的构造函数。
class dependent
{
private:
dependent(); // Implementation is optional
public:
dependent(const independent& sister)
{
// Initialize stuff
}
}
类的实例在其整个生命周期中都dependent
需要一个对象,independent
否则就没有意义。然后定义一个引用另一个对象的成员,再次使默认构造函数不可访问并定义您自己的构造函数。
class dependent
{
private:
dependent(); // No implementation
public:
dependent(const independent& sister): m_sister(sister)
{
// Initialize stuff
}
const independent& GetSister() const { return m_sister; }
void SetSister(const independent& sister) { m_sister = sister; }
private:
const independent& m_sister;
}
一个类的实例dependent
可能需要一个对象independent
才能工作,但没有一个对象仍然有意义。然后定义一个成员,它是指向另一个对象的指针。
class dependent
{
public:
dependent()
{
// Initialize stuff
}
const independent* GetSister() const { return m_sister; }
void SetSister(const independent* sister) { m_sister = sister; }
private:
const independent* m_sister;
}
现在让我们确保independent
对象准备就绪dependent
。
您可以使用指针和Init()
函数。
Independent* sister = NULL;
Dependent* brother = NULL;
Init()
{
assert(brother == NULL && sister == NULL);
sister = new independent();
brother = new dependent(sister);
}
您可以在函数中使用静态变量来强制执行创建顺序。这是可行的,但是拥有多个类的实例会很麻烦。
independent& GetSister()
{
static independent sister; // Initialized on first use
return sister;
}
dependent& GetBrother()
{
static dependent brother(GetSister()); // Initialized on first use
return brother;
}
最后,最干净的方法可能是尝试完全摆脱全局变量。然后,您将改用局部变量(您可以完全控制创建顺序)。当然,你必须通过需要它们的函数来传递它,如果它们很多,它会变得很麻烦。
但是,当您确定需要这些对象的所有代码时,将其移动到一个对象中可能是有意义的,并且您的两个原始全局变量将简单地成为该对象的成员。
class Foo
{
public:
Foo(): m_sister(...), m_brother(...)
{
// Initialize stuff
}
// Functions using the objects go here
private:
independent m_sister;
dependent m_brother;
}
嗯,我意识到我的答案很长,我有点得意忘形。这是外卖:
如果两个全局变量在一个编译单元中是按顺序排列的,那么它们将按顺序初始化。如果它们在不同的编译单元中处于不同的顺序(例如它们在不同的 .h 文件中被不同的 .cpp 包含在不同的顺序中),则结果是未定义的。
如果您必须按顺序排列它们,但不能将它们放在同一个编译单元中,请考虑使用单例模式。
class Independent
{
public:
static Independent& getO1()
{
static Independent o1;
return o1;
}
};
class Dependent
{
public:
static Dependent& getO2()
{
Independent::getO1(); // force o1 to exist first
static Dependent o2;
return o2;
}
};
这使用了静态局部变量在首次使用时初始化的规则,以确保顺序,而不管 .h 文件如何包含在内。
使用静态函数局部变量而不是全局变量。文件中的全局变量总是在调用任何函数之前初始化,因此可以使用非内联函数调用来强制按顺序初始化全局变量。这有一个额外的好处,您可以自由且安全地添加更多相互依赖的全局变量,并且它只是神奇地工作。
//header
class independent;
independent& o1();
class dependent;
dependent& o2();
//cpp1
independent& o1() {
static independent o;
return o;
}
//cpp2
dependent& o2() {
o1(); //affirm that o1 is constructed first.
static dependento; //now construct o2
return o;
}
int main() {
o1().thingy = -1;
o2().thingy = 3;
}
如果你有 n 个全局变量,其中 n>0,构造的逻辑位置是在包含 main() 的程序单元中。按所需顺序在文件中实例化它们。
无法保证它们的构建顺序。
消除这种依赖关系将是最好的选择,并且尽量不使用全局变量。但是,如果您必须并且如果您想确保其中一个在另一个之前,您最好坚持使用指针和返回指针的函数。
independent * pindependent = null;
dependent * pdependent = null;
independent * getIndependent()
{
if(pindependent == null)
pindependent = new independent(); // fixed the bug pointed to by the Mooing Duck
return pindependent;
}
dependent * getDependent()
{
if(pdependent == null)
{
getIndependent();
pdependent = new dependent(); // fixed the bug pointed to by the Mooing Duck
}
return pdependent;
}
在头文件中,您可以公开函数(可选地使用 extern):
extern independent * getIndependent();
extern dependent * getDependent();
使用全局指针和函数调用来保证正确的顺序:
//(!!PSEUDO-CODE!!)
Independent* o1;
Dependent* o2;
void CreateGlobalData();
void DestroyGlobalData();
//...
void CreateGlobalData() {
o1 = new Independent;
o2 = new Dependent(*o1); //This is an assumption that the
//Dependent type takes an Independent parameter.
}
//...
void DestroyGlobalData() {
delete o2; //Always destroy in reverse order!
delete o1;
}