10

我有一个类需要构造一个类B的实例:A

class B
{
    B(A* a); // there is no default constructor
};

现在我想创建一个包含B作为成员的类,所以我还需要添加A为成员并将其提供给B的构造函数:

class C
{
    C() : a(), b(&a) {}
    A a; // 1. initialized as a()
    B b; // 2. initialized as b(&a) - OK
};

但问题是,如果有人偶尔改变类中变量定义的顺序,就会破坏

class C
{
    C() : a(), b(&a) {}
    B b; // 1. initialized as b(&a) while "a" uninitialized
    A a; // too late...
};

A有没有不修改类和解决这个问题的好方法B?谢谢。

4

6 回答 6

7

有没有在不修改 A 类和 B 类的情况下解决这个问题的好方法?

打开编译器警告;对于 gcc,这是 -Wreorder(包含在 -Wall 中):

cc1plus:警告被视为错误
t.cpp:在构造函数'A::A()'中:
第 3 行:警告:'A::y' 将在之后初始化
第 3 行:警告:'int A::x'
第 2 行:警告:在此处初始化时

或者,使用类似 lint 的工具来检测这一点。


但问题是,如果有人偶尔改变类中变量定义的顺序……</p>

他们为什么要这样做?我怀疑你太担心发生什么了。即便如此,你也可以在课堂上发表评论:

A a;  // Must be listed before member 'b'!
B b;

不要低估合理评论的力量。:) 然后让故意无视他们的人得到他们应得的;毕竟,您使用的是 C++。

于 2011-02-15T21:25:39.527 回答
5

使用著名的 C++ 习语Base-from-Member来解决这个问题。

将基类定义为,

class C_Base
{
    A a; //moved `A a` to the base class!
    C_Base() : a() {}
};

class C : public C_Base
{
    C() : b(&a) {}
    B b; // 1. initialized as b(&a) while "a" uninitialized
    //A a; // too late...
};

现在,a保证在b.

于 2011-02-15T21:18:18.267 回答
2

将 b 存储在 unique_ptr 中,并将其设置在主体中,而不是在初始化列表中:

class C
{
    C() :a() {
        b = std::unique_ptr<B>(new B(&a));
    }
    A a;
    std::unique_ptr<B> b;
};
于 2011-02-15T21:30:02.390 回答
0

一种选择是不显式存储 A,而是使用动态分配来创建新的 A 以存储在 B 中:

class C {
public:
       C() : b(new A) {
           // handled in initialization list
       }
private:
       B b;
};

由于这保证了 A 在 B 之前创建,因此应该可以防止此问题发生。

于 2011-02-15T21:14:05.180 回答
0

问题是你在用第三个例子射击自己的脚。在 C++ 中,类/结构中成员变量的顺序很重要。无论您如何解决您的特定问题,如果由于糟糕的类设计/成员布局而将未初始化的数据传递给构造函数,您将使用未初始化的数据并可能获得未定义的行为,具体取决于到位的代码类型。

为了解决您的特定示例,如果B确实需要 anA并且关系是一对一的,那么为什么不创建一个新类,该类AB同时具有正确顺序的A对象和对象,并传递to的地址。那是:BAB

class AB
{
public:
  AB():b_(&a_) {}

private:
  A a_;
  B b_;
};

现在类可以通过使用而不是和C来避免排序问题:ABAB

class C
{
public:
  ...
private:
  AB ab_;
};

如前所述,这当然假设 和 之间存在 1:1 的A关系B。如果一个A对象可以被许多B对象共享,事情就会变得更加复杂。

于 2011-02-15T21:14:21.360 回答
0

我不确定您对 C 的实现和结构有多少控制,但有必要在 C 类中使用对象本身吗?您能否重新定义类以使用指针,然后将它们从初始化列表中移出,例如

class C
{
   C()
   {
     a = new A;
     b = new B(a);
   }
   ~C() {delete a; delete b;}

   A* a;
   B* b;
};

这避免了声明中的顺序问题,但为您提供了确保正确创建它们的新问题。此外,如果您经常创建很多C,初始化列表会稍微快一些。

于 2011-02-15T21:30:36.073 回答