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

时间:2013-09-12 02:09:34

标签: c++ class object constructor

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

class independent; 
class  dependent;
independent o1;
dependent o2;

为了确保在o2之前构造o1,我可以在o2之前声明o1。

这可以确保o1在o2之前构建吗?如果编译器改变了odering怎么办?

谢谢

7 个答案:

答案 0 :(得分:8)

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

因此,如果您的变量在同一个.cpp中声明,它们会按照您声明的顺序进行初始化,如果它们位于不同的文件中,您根本就不知道(我已经看到了非常的情况)积极的链接优化,无论情况如何都改变了静态变量初始化的顺序,但这不符合标准,并且不应该在正常使用情况下发生。)

核心问题

问题的背后是设计问题

  1. 如果你的一个类依赖于另一个类,那么它的结构应该明确地显示这个依赖,并且不可能在不满足依赖性的情况下进入。

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

  3. 您的问题没有提供足够的详细信息来了解哪种设计最适合您,因此以下是您可以做出决定的一些建议。

    依赖

    首先,让我们表达这种依赖......

    初始化时间依赖性

    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作为其构造函数中的参数

Don't make your code lie to you.

答案 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;
}