问题
I have the following classes:
class A
{
public:
A() { x = 0; std::cout<<"A default ctor()\n"; }
A(int x_) { x = x_; std::cout<<"A normal ctor()\n"; }
int x;
};
class B
{
public:
B() { std::cout<<"B ctor()\n"; }
private:
std::string str;
};
and a function which creates an object B, taking an object A as parameter:
B
createB(const A& a) {
std::cout<<"a int: "<<a.x<<"\n";
return B();
}
if I design a class C, which has members of type A and B and constructs the B-object before A-object is constructed but using the A object to do so, this will compile without warnings but it will silently enter a bug:
class C
{
public:
C(): b(createB(a)), a(10) {}
private:
B b;
A a;
};
int main()
{
C c;
return 0;
}
Of course, the above example is a trivial one, but I've seen it in real world, in much more complex code (it's Friday, 8:30 PM and I just fixed this bug which led to segfaults).
How can I prevent this from happening?
回答1:
I would agree with what others have suggested, namely that the onus is on the designer to ensure that objects are initialized before use. I see two ways of doing that in your case:
First (and easiest), reverse the order of a
and b
in the class definition:
class C
{
public:
C(): b(createB(a)), a(10) {}
private:
A a;
B b;
};
Second, you could move a
to a base class if you want to really emphasize that it's initialization occurs before that of other members:
class CBase
{
protected:
CBase(): a(10) {}
protected:
A a;
};
class C : private CBase
{
public:
C(): b(createB(a)) {}
private:
B b;
};
回答2:
I see three possible alternatives:
Require A
to be constructed prior to constructing C
:
class C
{
public:
C(const A& a) : a_(a), b_(a) {}
private:
A a_;
B b_;
};
Construct A
prior to constructing 'B':
Delaying the construction of B
until A
is complete. This stops the undefined behavior from happening, but doesn't enforce proper behavior via the interface (as options 1 and 3 do)
class C
{
public:
C() : a_(/* construct as appropriate */)
{
b_.reset(new B(a_));
}
private:
A a_;
std::unique_ptr<B> b_;
};
If the design allows for it, have B
contain (and expose) A
.
This appears possible from the trivial example, but in real life may not be:
class B
{
public:
const A& my_a() {return a_;}
private:
// construct as appropriate (?)
A a_;
};
class C
{
private:
B b_;
};
来源:https://stackoverflow.com/questions/13091561/how-to-prevent-usage-of-class-members-not-yet-constructed