在C ++中,我们有两个全局对象,它们都由不同文件中的其他对象定义。一个物体的构造取决于已经构建的另一个物体。
class independent;
class dependent;
independent o1;
dependent o2;
为了确保在o2之前构造o1,我可以在o2之前声明o1。
这可以确保o1在o2之前构建吗?如果编译器改变了odering怎么办?
谢谢
答案 0 :(得分:8)
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;
}
答案 1 :(得分:8)
A
取决于B
,则 A
应将B
作为其构造函数中的参数。答案 2 :(得分:6)
如果两个全局变量在编译单元中 >>,则它们将按顺序初始化。如果它们在不同的编译单元中处于不同的顺序(就像它们在不同的.hpp中包含在不同的顺序中的不同.h文件中),结果是未定义的。
如果您必须按顺序排列它们,但是您无法将它们放在同一个补充单元中,请考虑使用单例模式。
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文件如何被包含在内。
答案 3 :(得分:2)
使用静态函数本地而不是全局变量。在调用任何函数之前,文件中的全局变量始终被初始化,因此可以使用非内联函数调用来强制按顺序初始化全局变量。这有一个额外的好处,你可以自由,安全地添加更多的相互依赖的全局变量,它只是神奇地工作。
//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;
}
答案 4 :(得分:1)
如果你有n个全局变量,其中n> 0,那么构造的逻辑位置在包含main()的程序单元中。按所需顺序在文件中实例化它们。
答案 5 :(得分:0)
无法保证构建它们的顺序。
消除这种依赖性是最好的选择,也是尝试不使用全局变量。但是,如果你必须,如果你想确保其中一个在另一个之前,你最好坚持使用指针和返回指针的函数。
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();
答案 6 :(得分:0)
使用全局指针和函数调用来保证正确的顺序:
//(!!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;
}