我有一个类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
};
但问题是,如果有人偶尔更改类中变量 definition 的顺序,它将会中断
class C
{
C() : a(), b(&a) {}
B b; // 1. initialized as b(&a) while "a" uninitialized
A a; // too late...
};
有没有一种很好的方法可以在不修改类A
和B
的情况下解决这个问题?感谢。
答案 0 :(得分:7)
有没有一种好方法可以在不修改A类和B类的情况下解决这个问题?
开启compiler warnings;对于gcc,这是-Wreorder(包含在-Wall中):
cc1plus: warnings being treated as errors t.cpp: In constructor 'A::A()': Line 3: warning: 'A::y' will be initialized after Line 3: warning: 'int A::x' Line 2: warning: when initialized here
或者,使用类似lint的工具来检测它。
但问题是,如果有人偶尔改变类中变量定义的顺序......
他们为什么要这样做?我怀疑你太担心可能会发生什么。即便如此,您也可以在课堂上发表评论:
A a; // Must be listed before member 'b'!
B b;
不要低估良好评论的力量。 :)然后允许有意无视他们的人获得他们应得的东西;毕竟你正在使用C ++。
答案 1 :(得分:5)
使用名为Base-from-Member的众所周知的C ++习语来解决这个问题。
将基类定义为
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
之前初始化。
答案 2 :(得分:2)
将b存储在unique_ptr中,并将其设置在正文中,而不是在初始化列表中:
class C
{
C() :a() {
b = std::unique_ptr<B>(new B(&a));
}
A a;
std::unique_ptr<B> b;
};
答案 3 :(得分:0)
一种选择是不显式存储A,而是使用动态分配来创建一个新的A来存储在B中:
class C {
public:
C() : b(new A) {
// handled in initialization list
}
private:
B b;
};
由于这可以保证在B之前创建A,因此可以防止此问题发生。
答案 4 :(得分:0)
问题是你用第三个例子射击自己。在C ++中,类/结构中成员变量的顺序很重要。无论你如何解决你的特定问题,如果由于糟糕的类设计/成员布局而将未初始化的数据传递给构造函数,你将使用单元化数据并可能获得未定义的行为,具体取决于所使用的代码类型。
要解决您的特定示例,如果B
确实需要A
且关系是一对一,为什么不创建一个同时具有{{1}的新类AB
}对象和正确顺序的A
对象,并将B
的地址传递给A
。那就是:
B
现在,通过使用class AB
{
public:
AB():b_(&a_) {}
private:
A a_;
B b_;
};
代替C
和AB
,课程A
可以避免排序问题:
B
如前所述,这当然假设class C
{
public:
...
private:
AB ab_;
};
和A
之间存在1:1的关系。如果B
对象可以被许多A
个对象共享,那么事情会变得更加复杂。
答案 5 :(得分:0)
我不确定你对C的实现和结构有多少控制,但是有必要在C类中使用这些对象吗?您是否可以重新定义类以使用指针,然后将它们从初始化列表中移出,例如
class C
{
C()
{
a = new A;
b = new B(a);
}
~C() {delete a; delete b;}
A* a;
B* b;
};
这样可以避免声明中的订单问题,但会为您提供确保正确创建订单的新问题。此外,如果您经常创建C的 A LOT ,初始化列表会稍快一些。