Virtual functions in constructors. Is it possible?

A

Anonymous

Is it possible to call a virtual function as Init() in a constructor?

So that I could write something like this:

{
Base a ; // calls Base::Init
Derived b ; // calls Derived::Init
}

Ok, before answering I must admit you that I have a solution, but I am
just curious to know how many of you know the trick. When I saw it the
first time, I was really surprised about the power of C++ :)
 
A

Alf P. Steinbach /Usenet

* Anonymous, on 02.07.2011 00:19:
Is it possible to call a virtual function as Init() in a constructor?

So that I could write something like this:

{
Base a ; // calls Base::Init
Derived b ; // calls Derived::Init
}

Ok, before answering I must admit you that I have a solution, but I am just
curious to know how many of you know the trick. When I saw it the first time, I
was really surprised about the power of C++ :)

See the FAQ

<url: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6>


Cheers & hth.,

- Alf
 
A

Alf P. Steinbach /Usenet

* Anonymous, on 02.07.2011 01:19:
That is an ugly solution in my opinion.

It's not "a" solution, it's a discussion of the possible ways in current C++.

I think there is something much more elegant.. :)

Well, spit it out.


Cheers & hth.,

- Alf
 
A

Anonymous

Alf P. Steinbach /Usenet ha scritto:
* Anonymous, on 02.07.2011 01:19:

It's not "a" solution, it's a discussion of the possible ways in current
C++.

Ok, to be precise, the ways mentioned in the FAQ have the following
limitations:

"In this variation, the code that is creating the object *explicitly
executes both phases*. When executing Phase I, the code creating the
object either knows the object's exact class (e.g., new Derived() or
perhaps a local Derived object), or doesn't know the object's exact
class (e.g., the virtual constructor idiom or some other factory). The
"doesn't know" case is strongly preferred when you want to make it easy
to plug-in new derived classes."

and


"The next approach does not rely on a two-phase initialization, instead
using a second hierarchy whose only job is to house methods foo() and
bar(). *This approach doesn't always work, and in particular it doesn't
work in cases when foo() and bar() need to access the instance data
declared in Derived*, but it is conceptually quite simple and clean and
is commonly used."

In a way or the other static member functions are used there, which make
the approaches just "ugly".

They key to the *perfect solution* is to profit by the lifetime of
temporary objects passed to constructors: you can invoke a virtual
function init() after an object has been constructed by calling it from
a destructor of a temporary object passed to the constructor of the
class that has to be initialized.
 
A

Alf P. Steinbach /Usenet

* Anonymous, on 02.07.2011 10:50:
Alf P. Steinbach /Usenet ha scritto:

Ok, to be precise, the ways mentioned in the FAQ have the following limitations:

"In this variation, the code that is creating the object *explicitly executes
both phases*. When executing Phase I, the code creating the object either knows
the object's exact class (e.g., new Derived() or perhaps a local Derived
object), or doesn't know the object's exact class (e.g., the virtual constructor
idiom or some other factory). The "doesn't know" case is strongly preferred when
you want to make it easy to plug-in new derived classes."

and


"The next approach does not rely on a two-phase initialization, instead using a
second hierarchy whose only job is to house methods foo() and bar(). *This
approach doesn't always work, and in particular it doesn't work in cases when
foo() and bar() need to access the instance data declared in Derived*, but it is
conceptually quite simple and clean and is commonly used."

In a way or the other static member functions are used there, which make the
approaches just "ugly".

They key to the *perfect solution* is to profit by the lifetime of temporary
objects passed to constructors: you can invoke a virtual function init() after
an object has been constructed by calling it from a destructor of a temporary
object passed to the constructor of the class that has to be initialized.

So, if I do

p = new YourClass()

and init() fails, do I have a memory leak or not?

But I think the scheme that you're suggesting should be in that FAQ item.

I didn't think of it when I convinced Marshall to add that FAQ item (and neither
did he, or AFAIK anyone else), sorry.



Cheers & thanks,

- Alf
 
A

Anonymous

Alf P. Steinbach /Usenet ha scritto:
* Anonymous, on 02.07.2011 10:50:

So, if I do

p = new YourClass()

and init() fails, do I have a memory leak or not?

no, because init() is called from a destructor of an object passed as
argument to the constructor. From top down, if init() throws an
exception, then the destruction fails, then the construction fails..
But I think the scheme that you're suggesting should be in that FAQ item.

where exactly?
 
A

Alf P. Steinbach /Usenet

* Anonymous, on 02.07.2011 11:25:
Alf P. Steinbach /Usenet ha scritto:

no, because init() is called from a destructor of an object passed as argument
to the constructor. From top down, if init() throws an exception, then the
destruction fails, then the construction fails..

I think you're mistaken.

Could you post a complete smallest possible example?


Cheers & hth.,

- Alf
 
W

Werner

no, because init() is called from a destructor of an object passed as
argument to the constructor. From top down, if init() throws an
exception, then the destruction fails, then the construction fails..

Init() may not fail by throwing, since it's called from a
destructor, and throwing would imply terminating (15.2.3).

Kind Regards,

Werner
 
A

Anonymous

Werner ha scritto:
Init() may not fail by throwing, since it's called from a
destructor, and throwing would imply terminating (15.2.3).

Kind Regards,

Werner

Ok, after all I think that if necessary init() might handle failures
inside its body.

class VirtualBase {
protected:
class Initializer;
friend class Initializer;

class Initializer {
public:

Initializer() {
std::cout << "Initializer" << std::endl;
p = 0;
}

~Initializer() {
std::cout << "~Initializer" << std::endl;
if (p)
p->Init(); // call the virtual function
}
// 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) {
std::cout << "VirtualBase" << std::endl;
i.Register(this);
}
private:
virtual void Init() = 0;
// will be called immediately after the constructor
// of the most derived class
};

// This is the actual hierarchy

class Base : public virtual VirtualBase {
public:

Base(const VirtualBase::Initializer& i = Initializer()) :
VirtualBase(i) {
std::cout << "Base" << std::endl;
}

~Base() {
std::cout << "~Base" << std::endl;
}

void Init() {
std::cout << "Base::Init()" << std::endl;
}
};

class Derived : public Base {
public:
// Derived() : Base() {} // compile-time error as wanted

Derived(const VirtualBase::Initializer& i = Initializer()) :
Base(i), /* Base(i) is optional...*/
VirtualBase(i)
{
std::cout << "Derived" << std::endl;
}

~Derived() {
std::cout << "~Derived" << std::endl;
}

void Init() {
std::cout << "Derived::Init()" << std::endl;
}
};


int main() {
Base x; // calls Base::Init
Derived y ; // calls Derived::Init
return( 0 ) ;
}
 
A

Alf P. Steinbach /Usenet

* Anonymous, on 04.07.2011 20:41:
class VirtualBase {
protected:
class Initializer;
friend class Initializer;

class Initializer {
public:

Initializer() {
std::cout << "Initializer" << std::endl;
p = 0;
}

~Initializer() {
std::cout << "~Initializer" << std::endl;
if (p)
p->Init(); // call the virtual function
}
// 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) {
std::cout << "VirtualBase" << std::endl;
i.Register(this);
}
private:
virtual void Init() = 0;
// will be called immediately after the constructor
// of the most derived class
};

// This is the actual hierarchy

class Base : public virtual VirtualBase {
public:

Base(const VirtualBase::Initializer& i = Initializer()) : VirtualBase(i) {
std::cout << "Base" << std::endl;
}

~Base() {
std::cout << "~Base" << std::endl;
}

void Init() {
std::cout << "Base::Init()" << std::endl;
}
};

class Derived : public Base {
public:
// Derived() : Base() {} // compile-time error as wanted

Derived(const VirtualBase::Initializer& i = Initializer()) :
Base(i), /* Base(i) is optional...*/
VirtualBase(i)
{
std::cout << "Derived" << std::endl;
}

~Derived() {
std::cout << "~Derived" << std::endl;
}

void Init() {
std::cout << "Derived::Init()" << std::endl;
}
};


int main() {
Base x; // calls Base::Init
Derived y ; // calls Derived::Init
return( 0 ) ;
}

I reworked your example to show off the memory leak problem that I mentioned.

In addition this led me to realize one problem that I didn't think of earlier,
namely that in the event of a failing constructor, the Initializer destructor
will be calling a virtual method on a non-existent object, invoking UB.

Here's the reworked code, just play with the macro symbols NO_INIT_CALL,
FAIL_CONSTRUCTOR and FAIL_INIT:


<code>
#include <iostream>
#include <stdexcept>
#include <stdlib.h>
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;
#ifndef NO_INIT_CALL
if (p)
p->Init(); // call the virtual function
#endif
}
// 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
};

// This is the actual hierarchy

class Base : public virtual VirtualBase {
public:

static void* operator new( size_t size )
{
cout << "Allocation" << endl;
++g::nObjectsAllocated;
return ::eek:perator new( size );
}

static void operator delete( void* p )
{
cout << "Deallocation" << endl;
--g::nObjectsAllocated;
return ::eek:perator delete( p );
}

Base(const VirtualBase::Initializer& i = Initializer()) : VirtualBase(i) {
cout << "Base" << endl;
#ifdef FAIL_CONSTRUCTOR
throw runtime_error( "Oops, `Base::Base` failed." );
#endif
}

~Base() {
cout << "~Base" << endl;
}

void Init() {
cout << "Base::Init()" << endl;
#ifdef FAIL_INIT
throw runtime_error( "Oops, `Init` failed." );
#endif
}
};

int main()
{
int result = EXIT_FAILURE;
try
{
Base* p = new Base; // calls Base::Init
cout << endl;
delete p;
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>


As you can see the scheme, at least in its current incarnation, is not very
exception-friendly: invoking UB, and leaking memory.

Can it be made exception safe?


Cheers & hth.,

- Alf
 
L

Leo Equinox Gaspard

Le 04/07/2011 21:35, Alf P. Steinbach /Usenet a écrit :
* Anonymous, on 04.07.2011 20:41:

I reworked your example to show off the memory leak problem that I
mentioned.

In addition this led me to realize one problem that I didn't think of
earlier, namely that in the event of a failing constructor, the
Initializer destructor will be calling a virtual method on a
non-existent object, invoking UB.

Here's the reworked code, just play with the macro symbols NO_INIT_CALL,
FAIL_CONSTRUCTOR and FAIL_INIT:


<code>
#include <iostream>
#include <stdexcept>
#include <stdlib.h>
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;
#ifndef NO_INIT_CALL
if (p)
p->Init(); // call the virtual function
#endif
}
// 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
};

// This is the actual hierarchy

class Base : public virtual VirtualBase {
public:

static void* operator new( size_t size )
{
cout << "Allocation" << endl;
++g::nObjectsAllocated;
return ::eek:perator new( size );
}

static void operator delete( void* p )
{
cout << "Deallocation" << endl;
--g::nObjectsAllocated;
return ::eek:perator delete( p );
}

Base(const VirtualBase::Initializer& i = Initializer()) : VirtualBase(i) {
cout << "Base" << endl;
#ifdef FAIL_CONSTRUCTOR
throw runtime_error( "Oops, `Base::Base` failed." );
#endif
}

~Base() {
cout << "~Base" << endl;
}

void Init() {
cout << "Base::Init()" << endl;
#ifdef FAIL_INIT
throw runtime_error( "Oops, `Init` failed." );
#endif
}
};

int main()
{
int result = EXIT_FAILURE;
try
{
Base* p = new Base; // calls Base::Init
cout << endl;
delete p;
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>


As you can see the scheme, at least in its current incarnation, is not
very exception-friendly: invoking UB, and leaking memory.

Can it be made exception safe?


Cheers & hth.,

- Alf

Couldn't you use, in ~Initializer :

try {
if (p)
p->Init();
} catch(...) {
p->InitFailed();
throw;
}

And implement an InitFailed() virtual method that acts as the countrary
(not sure of the word to use there) of Init ?

So the code should have no more memory leaks.

Cheers,
Leo
 
A

Alf P. Steinbach /Usenet

* Leo Equinox Gaspard, on 04.07.2011 23:59:
Couldn't you [one] use, in ~Initializer :

try {
if (p)
p->Init();
} catch(...) {
p->InitFailed();
throw;
}

No.

This could handle exceptions from Init in the sense of reacting to them, at the
cost of creating a zombie object (unusable object) when such an exception occurs.

But for this case it would still yield a memory leak, because this all happens
after the new-expression has been fully evaluated. When the exception occurs in
the constructor, the new-expression can clean up (and does). When the exception
occurs after the new-expression is fully evaluated, which is the case with the
call of Init, the new-expression is already out of the picture, in the past.

And it can't handle the case where the construction of the object that p is
pointing to, throws an exception.

For then, by the time the ~Initializer runs (at the end of the full-expression
that instantiates the object), the exception has propagated out of the
new-expression and, due to the new-expression's cleanup, the object that p
points to has been destroyed, hence, the call 'p->Init()' is now calling a
virtual function on a non-existent object, which is Undefined Behavior. With g++
the error is caught by the runtime, and you get a crash. With MSVC it's not
caught, and you get more subtle errors.

And implement an InitFailed() virtual method that acts as the countrary (not
sure of the word to use there) of Init ?

So the code should have no more memory leaks.

Sorry, no, see above.


Cheers & hth.,

- Alf
 
L

Leo Equinox Gaspard

Le 05/07/2011 11:09, Alf P. Steinbach /Usenet a écrit :
* Leo Equinox Gaspard, on 04.07.2011 23:59:
Couldn't you [one] use, in ~Initializer :

try {
if (p)
p->Init();
} catch(...) {
p->InitFailed();
throw;
}

No.

This could handle exceptions from Init in the sense of reacting to them,
at the cost of creating a zombie object (unusable object) when such an
exception occurs.

But for this case it would still yield a memory leak, because this all
happens after the new-expression has been fully evaluated. When the
exception occurs in the constructor, the new-expression can clean up
(and does). When the exception occurs after the new-expression is fully
evaluated, which is the case with the call of Init, the new-expression
is already out of the picture, in the past.

And it can't handle the case where the construction of the object that p
is pointing to, throws an exception.

For then, by the time the ~Initializer runs (at the end of the
full-expression that instantiates the object), the exception has
propagated out of the new-expression and, due to the new-expression's
cleanup, the object that p points to has been destroyed, hence, the call
'p->Init()' is now calling a virtual function on a non-existent object,
which is Undefined Behavior. With g++ the error is caught by the
runtime, and you get a crash. With MSVC it's not caught, and you get
more subtle errors.

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 ?

Cheers,
Leo
 
A

Alf P. Steinbach /Usenet

* Leo Equinox Gaspard, on 05.07.2011 13:15:
Le 05/07/2011 11:09, Alf P. Steinbach /Usenet a écrit :
* Leo Equinox Gaspard, on 04.07.2011 23:59:
Couldn't you [one] use, in ~Initializer :

try {
if (p)
p->Init();
} catch(...) {
p->InitFailed();
throw;
}

No.

This could handle exceptions from Init in the sense of reacting to them,
at the cost of creating a zombie object (unusable object) when such an
exception occurs.

But for this case it would still yield a memory leak, because this all
happens after the new-expression has been fully evaluated. When the
exception occurs in the constructor, the new-expression can clean up
(and does). When the exception occurs after the new-expression is fully
evaluated, which is the case with the call of Init, the new-expression
is already out of the picture, in the past.

And it can't handle the case where the construction of the object that p
is pointing to, throws an exception.

For then, by the time the ~Initializer runs (at the end of the
full-expression that instantiates the object), the exception has
propagated out of the new-expression and, due to the new-expression's
cleanup, the object that p points to has been destroyed, hence, the call
'p->Init()' is now calling a virtual function on a non-existent object,
which is Undefined Behavior. With g++ the error is caught by the
runtime, and you get a crash. With MSVC it's not caught, and you get
more subtle errors.

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
 
L

Leo Equinox Gaspard

Le 05/07/2011 14:51, Alf P. Steinbach /Usenet a écrit :
* Leo Equinox Gaspard, on 05.07.2011 13:15:

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 ::eek:perator new( size );
}

static void operator delete( void* p )
{
cout << "Deallocation" << endl;
--g::nObjectsAllocated;
return ::eek:perator 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
 
A

Anonymous

Leo Equinox Gaspard ha scritto:
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) :

This is what I meant in my previous post. IMHO it should be ok.
 
A

Alf P. Steinbach /Usenet

* Leo Equinox Gaspard, on 05.07.2011 19:55:
<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 ::eek:perator new( size );
}

static void operator delete( void* p )
{
cout << "Deallocation" << endl;
--g::nObjectsAllocated;
return ::eek:perator 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 ?

Your code

* has Undefined Behavior if a constructor throws, and
* leaks memory without the auto_ptr

(while there's no leak for an ordinary single stage construction, because the
new-expression then cleans up).

The UB is critical, so this technique is only applicable for guaranteed no-throw
constructors, which except for default constructors are pretty rare beasts nowadays.

I don't know what you think you're mistaken about, sorry, but hopefully it's one
of the two points above?


Cheers & hth.,

- Alf
 
V

Virchanza

I've been playing around with GCC, and it seems as though GCC changes
an object's V-Table pointer while the object is constructed from the
ground up (from Base class to furthermost Derived class).

Here's the code I tried:

#include <iostream>
using std::cout;

class Base {

private:

static void Call_Init(Base *const p)
{
p->Init();
}

public:

virtual void Init()
{
cout << "This is Base::Init\n";
}

Base()
{
Call_Init(this);
}
};

class Derived : public Base {

public:

virtual void Init()
{
cout << "This is Derived::Init\n";
}
};

int main()
{
Base a;
Derived b;
}

The output from this program is:

This is Base::Init
This is Base::Init

I think there's two possible explanations for this:

1) The "Call_Init" function is expanded inline and is not actually
consulting the object's V-Table pointer.

2) The "Call_Init" function does actually consult the object's V-Table
pointer, but when Call_Init is called, the object's V-Table pointer
points to the V-Table for the Base Class. Presumably then, the
object's V-Table pointer gets updated as each derived class undergoes
construction.

Is Number 2 the norm for all C++ compilers?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top