在C++中,如何确保一个对象在另一个对象之前构造

in C++, how to make sure that one object is constructed before another object ?

本文关键字:一个对象 C++ 何确保 确保      更新时间:2023-10-16

在C++中,我们有两个全局对象,都由其他人在不同的文件中定义。一个对象的构造取决于另一个已经构造的对象。

class independent; 
class  dependent;
independent o1;
dependent o2;
为了确保 o1 在 o2 之前

构造,我可以通过在 o2 之前声明 o1 来做到这一点。

这能保证 o1 在 o2 之前构建吗?如果编译器改变结果怎么办?

谢谢

C++ 标准保证同一编译单元中静态变量的顺序初始化,但顺序在单独的编译单元中未定义。这通常被称为"静态初始化顺序惨败"。

因此,如果您的变量以相同的.cpp声明,它们将按照您声明它们的顺序进行初始化,如果它们在单独的文件中,您根本不知道(我见过非常激进的链接优化的情况,无论情况如何都更改了静态变量初始化的顺序,但这不符合标准, 并且不应该在正常用例中发生)。

核心问题

你的问题背后是一个设计问题

  1. 如果其中一个类依赖于另一个类,那么它的结构应该显式显示此依赖关系,并且无法进入不满足依赖关系的情况。

  2. 如果你有一个全局对象依赖于另一个你没有明确控制的全局对象,那么依赖的对象可能不应该是全局对象。

您的问题没有提供足够的细节来了解哪种设计最适合您,因此这里有一些建议可供您做出决定。

屬地

所以首先,让我们表达一下这种依赖关系...

初始化时间依赖性

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();
相关文章: