在C++中,如何确保一个对象在另一个对象之前构造
in C++, how to make sure that one object is constructed before another object ?
在C++中,我们有两个全局对象,都由其他人在不同的文件中定义。一个对象的构造取决于另一个已经构造的对象。
class independent;
class dependent;
independent o1;
dependent o2;
为了确保 o1 在 o2 之前构造,我可以通过在 o2 之前声明 o1 来做到这一点。
这能保证 o1 在 o2 之前构建吗?如果编译器改变结果怎么办?
谢谢
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;
}
结论
嗯,我意识到我的答案很长,我有点得意忘形。以下是要点:
- 尽可能避免使用全局变量。
- 表示类之间的依赖关系。
- 不要犹豫,将代码和状态变量封装为一个类,如果让事情变得更容易,它最终会有意义。
> 简单:如果A
依赖于B
那么A
应该将B
作为其构造函数中的参数。
不要让你的代码对你撒谎。
如果两个全局变量在一个编译单元中按顺序排列,则它们将按顺序初始化。 如果它们在不同的编译单元中处于不同的顺序中(例如它们在不同的 .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 () 的程序单元中。按所需顺序在文件中实例化它们。
使用全局指针和函数调用来保证正确的顺序:
//(!!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;
}
不能保证它们的构造顺序。
消除这种依赖关系将是最好的选择,并尽量不使用全局变量。但是,如果您必须并且想要确保其中一个先于另一个,则最好坚持使用指针和返回指针的函数。
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();
- 为什么我不能将一个对象push_back到属于另一个类的对象向量中?
- 在他自己的方法中,有可能将一个对象取消引用到另一个对象吗
- 从多个源构造一个对象,包括一个对象向量
- 为什么C++在将一个对象复制到另一个对象时需要对这两个对象进行低级常量限定
- 检查哪个对象调用了另一个对象的对象方法
- C++ 如何在将新对象分配给另一个对象时创建新对象
- 如何处理从一个对象传递到另一个在C++中具有公共抽象类的对象的消息
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 包装一个对象并假装它是一个 int
- 为什么我可以在不重载 "=" 运算符的情况下将一个对象分配给另一个对象?
- 如何在qt中将信号和插槽与另一个对象连接 --解决了
- 如何使用函数将一个对象的输入复制到另一个对象中
- 选择一个元素而不是一个对象的数组的原因
- 为什么我可以使用 memcpy 将一个对象变量复制到另一个对象变量
- 如何将包含另一个对象向量的对象保存到文件中,并使用C++中的二进制文件从文件中读回?
- 我重载了 << 和 = 运算符。为什么当我将一个对象分配给另一个对象并尝试打印它时,我会被打印出来?
- 打印对象的映射,其中另一个对象作为键
- 如何通过另一个对象中的命令正确地从一个对象返回数据
- 为什么将两个对象分配给另一个对象后,两个对象不一样?
- 绘制一个对象,比较模具缓冲区的两个不同值