I'm asking if (and why) the following approach is a) legal and b) moral. I'm asking with emphasis on C++03, but notes on C++11 are welcome, too. The idea is to prevent derived classes that could themselves be default constructible from implementing stupid B::B(int foo) : A(foo) {}
constructors.
class Base {
private:
int i;
Base(int i) : i(i) {}
protected:
Base() {}
public:
static Base* create(int i);
};
class Derived : public Base {
};
Base* Base::create(int i) {
Derived* d = new Derived();
Base* b = static_cast<Base*>(d);
delete b;
new(b) Base(i);
return d;
}
My gut is telling me, that something is fishy here. If any Derived
class accesses Base
members in its constructor, I want to be somewhere else, but otherwise I have trouble seeing valid reasons why the approach is bad.
Anyway, if you think this is an acceptable approach, how would to deal with reference members (something like int& Base::j
)?
Note: This is a follow-up question to How can I fake constructor inheritance in C++03?.
Edit: I must have been distracted when posting the question. Of course, instead of delete b
I meant b->~Base()
. I blame low blood sugar!
The code is incorrect and triggers undefined behavior. Your
Base
class does not have a virtual destructor, which means that thedelete b
will cause Undefined Behavior.The reasons for UB in the call to
delete
range from the fact that it will not release the derived resources (which seems to be the purpose of the code, ouch!), to the fact that it will try to release the allocated memory, which might work or not, depending on the layout of both objects. If it fails to deallocate the memory it will probably crash, if it succeeds the placement new call in the following line will try to initialize an object in memory that has already been released...Even if you changed the code (trying to avoid deallocation problems) to:
Where there is no
delete
and thus that particular source of Undefined Behavior is gone, the code is still undefined behavior (probably in too many ways to even mention). For once the call to the destructor is still UB, even if that was not, the fact that you recreated theBase
type means that dynamic dispatch for that object will probably consider the object to be aBase
rather than aDerived
object (in the case ofvtable
s, thevptr
that points to the RunTime Type information will refer toBase
, rather thanDerived
)And there are probably two or three other things that can go wrong and I cannot think of right now...