3

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

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

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

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

谢谢

4

7 回答 7

8

很简单:如果A依赖BA应该B在其构造函数中作为参数

不要让你的代码欺骗你。

于 2013-09-12T04:17:10.770 回答
8

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

结论

嗯,我意识到我的答案很长,我有点得意忘形。这是外卖:

  • 尽可能避免使用全局变量。
  • 表达类之间的依赖关系。
  • 不要犹豫,将代码和状态变量封装在一个类中,如果让事情变得更容易,它最终会有意义。
于 2013-09-12T04:09:46.373 回答
6

如果两个全局变量在一个编译单元中是按顺序排列的,那么它们将按顺序初始化。如果它们在不同的编译单元中处于不同的顺序(例如它们在不同的 .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 文件如何包含在内。

于 2013-09-12T02:24:30.240 回答
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;
}
于 2013-09-12T03:17:00.073 回答
1

如果你有 n 个全局变量,其中 n>0,构造的逻辑位置是在包含 main() 的程序单元中。按所需顺序在文件中实例化它们。

于 2013-09-12T03:13:24.243 回答
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();
于 2013-09-12T02:35:57.670 回答
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;
}
于 2013-09-12T03:11:11.793 回答