Re: Virtual functions in constructors. Is it possible?
Le 05/07/2011 14:51, Alf P. Steinbach /Usenet a ?crit :
* Leo Equinox Gaspard, on 05.07.2011 13:15:
Oh. I thought the exception was in Init().
The run of the program I figured was :
=> new Derived()
=> Instantiation of an Initializer
=> Only do it, we don't want *any* exception in this constructor
=> End of new
=> ~Initializer()
=> Call to Init(), catching exceptions
=> Init() does the whole construction stuff, additionnally calling
Base's Init()
=> If any exception was caught, then clear the work done by Init()
using InitFailed()
=> InitFailed() should be exception-less and make the object ready to
be destructed.
=> Then rethrow the exception
=> End of ~Initializer
=> If any exception is thrown, then the object is not valid, and hence
should
not be used, but immediately destructed. That's the default behavior
of the
exception, which will make any scoped items to be deleted.
Of course, using a raw pointer will lead to memory leaks, but that's
always as
it with them plus exceptions.
Where am I mistaking ?
Instead of me explaining *again*, try out the given code. You can use
compiler switch "-D" to define a macro symbol.
Cheers & hth.,
- Alf
I *did* try it.
And, as soon as I change it to reflect what I mean - this means removing the
FAIL_CONSTRUCTOR case and adding a InitFailed() -, everything works OK. BTW
the NO_INIT_CALL looks useless to me, so I removed it.
And, for the -D switch, thanks, but I knew it.
Please also note that InitFailed doesn't has any use, but it could be
used in
case of raw pointer manipulation.
The code (you only have to play with FAIL_INIT) :
<code>
#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <memory>
using namespace std;
namespace g {
int nObjectsAllocated = 0;
} // namespace g
class VirtualBase {
protected:
class Initializer;
friend class Initializer;
class Initializer {
public:
Initializer() {
cout << "Initializer" << endl;
p = 0;
}
~Initializer() {
cout << "~Initializer" << endl;
try {
if (p)
p->Init(); // call the virtual function
} catch (...) {
p->InitFailed();
throw;
}
}
// might be private if VirtualBase is declared as friend...
void Register(VirtualBase* b) const {
p = b;
}
private:
mutable VirtualBase* p;
// private and not implemented
Initializer(const Initializer&);
void operator =(const Initializer&);
};
public:
VirtualBase(const Initializer& i) {
cout << "VirtualBase" << endl;
i.Register(this);
}
private:
virtual void Init() = 0;
// will be called immediately after the constructor
// of the most derived class
virtual void InitFailed() = 0;
// will be called if Init failed. Should leave the object
// in a destructable state.
};
// This is the actual hierarchy
class Base : public virtual VirtualBase {
public:
static void* operator new( size_t size )
{
cout << "Allocation" << endl;
++g::nObjectsAllocated;
return ::operator new( size );
}
static void operator delete( void* p )
{
cout << "Deallocation" << endl;
--g::nObjectsAllocated;
return ::operator delete( p );
}
Base(const VirtualBase::Initializer& i = Initializer()) :
VirtualBase(i) {
cout << "Base" << endl;
}
~Base() {
cout << "~Base" << endl;
}
void Init() {
cout << "Base::Init()" << endl;
#ifdef FAIL_INIT
throw runtime_error( "Oops, `Init` failed." );
#endif
}
void InitFailed() {
cout << "Base::InitFailed() - could do something if needed" <<
endl;
}
};
int main()
{
int result = EXIT_FAILURE;
try
{
auto_ptr<Base> p(new Base); // calls Base::Init.
// We *need* an auto_ptr to ensure that the item will be
// deleted in case of exception.
result = EXIT_SUCCESS;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
}
if( g::nObjectsAllocated > 0 )
{
cerr << "!Memory leak." << endl;
}
cout << "Finished." << endl;
return result;
}
</code>
So I must repeat my question : Where am I mistaking ?
Cheers,
Leo