Discussion:
Calling pure virtual function from destructor
(too old to reply)
Dan Mattsson
2009-11-11 09:25:03 UTC
Permalink
I've been searching for a solution (or, rather, the reason) for an issue
I've been having. Our software makes ample use of inheritance in all kinds
of manners but one thing struck me as odd.

There's an abstract base class (let's call it CEdsDocument, since it is its
name) that defines a few common functions and some abstract ones:

class CEdsDocument abstract : public CDocument
{
public:
CEdsDocument();
virtual ~CEdsDocument();
[cut for brevity et al.]
virtual afx_msg void OnStopExec() abstract;
}

CEdsDocument::~CEdsDocument()
{
OnStopExec();
[Bunch of other stuff]
}

The compiler (VC++ 9) accepts it but the linker won't since there is no
CEdsDocument::OnStopExec(). But, whenever the destructor is called, the
function will exist since it's pure virtual.

Then I saw Herb Sutters article at http://www.gotw.ca/gotw/031.htm and it
got me thinking. Providing a body for OnStopExec() doesn't help since the
abstract base class' version of the function gets called.

Is there a way to call a pure virtual function from the destructor of the
abstract class that declared it? Or is it Just Plain WrongT?
David Webber
2009-11-11 09:36:52 UTC
Permalink
Post by Dan Mattsson
I've been searching for a solution (or, rather, the reason) for an issue
I've been having.
r.12.7 (in my ancient copy of Stroustrup): you can't call functions in
derived classes during construction and destruction, (as otherwise you might
be calling memners of unconstructed/destroyed objects).

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mozartists/mailinglist.htm
Ulrich Eckhardt
2009-11-11 10:15:41 UTC
Permalink
Post by Dan Mattsson
There's an abstract base class (let's call it CEdsDocument, since it is
class CEdsDocument abstract : public CDocument
{
CEdsDocument();
virtual ~CEdsDocument();
[cut for brevity et al.]
virtual afx_msg void OnStopExec() abstract;
}
CEdsDocument::~CEdsDocument()
{
OnStopExec();
[Bunch of other stuff]
}
The compiler (VC++ 9) accepts it but the linker won't since there is no
CEdsDocument::OnStopExec(). But, whenever the destructor is called, the
function will exist since it's pure virtual.
No. Try using 'typeid(*this)' in the costructor or destructor of the
baseclass and derived class. You will see that the actual type of the
object changes as it is being constructed or destroyed. Similarly, since it
_is_ an instance of the baseclass and not the derived class, there is no
implementation for the referenced function.
Post by Dan Mattsson
Is there a way to call a pure virtual function from the destructor of the
abstract class that declared it?
No.
Post by Dan Mattsson
Or is it Just Plain WrongT?
I've come across the need for similar things. What I usually did was to
store a flag whether some cleanup was performed and simply assert() that it
is set in the baseclass' destructor, alongside an explaining comment that
it must be done by the derived class.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Stuart Redmann
2009-11-11 10:59:38 UTC
Permalink
Dan Mattsson wrote:
[snip]
Post by Dan Mattsson
Is there a way to call a pure virtual function from the destructor of the
abstract class that declared it? Or is it Just Plain WrongT?
It's just plain wrong due to the design of C++. However, there are
work-arounds for specific cases (e.g. when the classes involved
provide default-constructors). Here is a small example that should get
you going:

#include <iostream>

/* Example base class.
*/
class CBase
{
public:
virtual ~CBase () {}

/* Should be provided by derived classes. This method will be called
* on the destruction of the object.
*/
virtual void OnShutDown () = 0;

protected:
/* This method should never be provided by derived classes. The sole
* purpose of this method is that the user is forced to use the
class
* CAdapterClass (see below) for creating instances of CBase-derived
classes.
*/
virtual void I_MADE_A_MISTAKE_BECAUSE_I_OVERWROTE_THIS_METHOD () =
0;
};

/* Example class that is derived from CBase.
*
* Note that this class should have a default constructor, or else we
won't
* be able to create an instance of this class through the adapter
class
* CAdapterClass (see
CBase::I_MADE_A_MISTAKE_BECAUSE_I_OVERWROTE_THIS_METHOD).
*/
class CDerived : public CBase
{
public:
virtual void OnShutDown ()
{
std::cout << "CDerived::OnShutdown called" << std::endl;
}
};

/* This adapter class can be used to instantiate objects of CBase-
derived classes.
* This adapter will ensure that the method OnShutDown will be called
for the
* most-derived class during the destruction of the instance.
*/
template<class t_BaseClass>
class CAdapterClass : public t_BaseClass
{
public:
virtual ~CAdapterClass ()
{
OnShutDown ();
}

protected:
virtual void I_MADE_A_MISTAKE_BECAUSE_I_OVERWROTE_THIS_METHOD ()
{
// There is nothing actually to be done here.
}
};

int main(int argc, char* argv[])
{
// The following is no longer possible, since CDerived does not
provide a method
// called I_MADE_A_MISTAKE_BECAUSE_I_OVERWROTE_THIS_METHOD.

//CDerived Derived;

// This works: The adapter class provides the method
// I_MADE_A_MISTAKE_BECAUSE_I_OVERWROTE_THIS_METHOD.
CAdapterClass<CDerived> Derived;
return 0;
}

This approach seems to have to faults:
(A) Nothing can prevent the writer of CDerived from providing his own
version of I_MADE_A_MISTAKE_BECAUSE_I_OVERWROTE_THIS_METHOD (at least
I see no way to prevent him).

(B) The adapter class will only work when the CDerived class provides
a deault constructor. This can be overcome by stuffing the class
CAdapterClass with constructors that will be needed throughout the
project:

template<class t_BaseClass>
class CAdapterClass : public t_BaseClass
{
public:
CAdapterClass ()
: t_BaseClass ()
{}

// For classes that have a constructor taking an int.
CAdapterClass (int i)
: t_BaseClass (i)
{}

// For classes that have a constructor taking a float.
CAdapterClass (float f)
: t_BaseClass (f)
{}
and so on. Note that the derived class does not have to provide all
the constructors of CAdapterClass since CAdapterClass is a template.


Regards,
Stuart
Vladimir Grigoriev
2009-11-11 12:43:13 UTC
Permalink
You may call a pure virtual function if it has a body, i.e. it should be
defined. For examplr

class Base

{

protected:

virtual ~Base() = 0;

private:

virtual void f() const = 0;

};

inline Base::~Base()

{

std::cout << "Inside Base::~Base()\n";

f();

}

inline void Base::f() const

{

std::cout << "Inside Base::f()\n";

}


class Derived: public Base

{

public:

virtual ~Derived()

{

std::cout << "Inside Derived::~Derived()\n";

f();

}

private:

virtual void f() const

{

std::cout << "Inside Derived::f()\n";

}

};


int _tmain(int argc, _TCHAR* argv[])

{

{

Derived d;

}

return 0;
}

In your case the linker does not have the definition of the function.

Vladimir Grigoriev
Post by Dan Mattsson
I've been searching for a solution (or, rather, the reason) for an issue
I've been having. Our software makes ample use of inheritance in all kinds
of manners but one thing struck me as odd.
There's an abstract base class (let's call it CEdsDocument, since it is
class CEdsDocument abstract : public CDocument
{
CEdsDocument();
virtual ~CEdsDocument();
[cut for brevity et al.]
virtual afx_msg void OnStopExec() abstract;
}
CEdsDocument::~CEdsDocument()
{
OnStopExec();
[Bunch of other stuff]
}
The compiler (VC++ 9) accepts it but the linker won't since there is no
CEdsDocument::OnStopExec(). But, whenever the destructor is called, the
function will exist since it's pure virtual.
Then I saw Herb Sutters article at http://www.gotw.ca/gotw/031.htm and it
got me thinking. Providing a body for OnStopExec() doesn't help since the
abstract base class' version of the function gets called.
Is there a way to call a pure virtual function from the destructor of the
abstract class that declared it? Or is it Just Plain WrongT?
Dan Mattsson
2009-11-11 15:29:04 UTC
Permalink
Thank you, all - the reason is now pretty clear to me. In the end, I
implemented the cleanup in each derived class - a few more lines of code and
duplication of functionality but sometimes it hurts trying to be too
clever...

BR,
Dan
Post by Vladimir Grigoriev
You may call a pure virtual function if it has a body, i.e. it should be
defined. For examplr
class Base
{
virtual ~Base() = 0;
virtual void f() const = 0;
};
inline Base::~Base()
{
std::cout << "Inside Base::~Base()\n";
f();
}
inline void Base::f() const
{
std::cout << "Inside Base::f()\n";
}
class Derived: public Base
{
virtual ~Derived()
{
std::cout << "Inside Derived::~Derived()\n";
f();
}
virtual void f() const
{
std::cout << "Inside Derived::f()\n";
}
};
int _tmain(int argc, _TCHAR* argv[])
{
{
Derived d;
}
return 0;
}
In your case the linker does not have the definition of the function.
Vladimir Grigoriev
Post by Dan Mattsson
I've been searching for a solution (or, rather, the reason) for an issue
I've been having. Our software makes ample use of inheritance in all
kinds of manners but one thing struck me as odd.
There's an abstract base class (let's call it CEdsDocument, since it is
class CEdsDocument abstract : public CDocument
{
CEdsDocument();
virtual ~CEdsDocument();
[cut for brevity et al.]
virtual afx_msg void OnStopExec() abstract;
}
CEdsDocument::~CEdsDocument()
{
OnStopExec();
[Bunch of other stuff]
}
The compiler (VC++ 9) accepts it but the linker won't since there is no
CEdsDocument::OnStopExec(). But, whenever the destructor is called, the
function will exist since it's pure virtual.
Then I saw Herb Sutters article at http://www.gotw.ca/gotw/031.htm and it
got me thinking. Providing a body for OnStopExec() doesn't help since
the abstract base class' version of the function gets called.
Is there a way to call a pure virtual function from the destructor of the
abstract class that declared it? Or is it Just Plain WrongT?
legalize+ (Richard)
2009-11-11 18:51:51 UTC
Permalink
[Please do not mail me a copy of your followup]
Post by Dan Mattsson
I've been searching for a solution (or, rather, the reason) for an issue
I've been having. Our software makes ample use of inheritance in all kinds
of manners but one thing struck me as odd.
The problem is similar to two-phase construction, only in your case
its two-phase destruction.

With two-phase construction, you make c'tors inaccessible to clients
and make them go through a factory function to get instances. The
factory function can then do the two-phase construction: new up the
object (phase 1) and then initialize it (phase 2).

With two-phase destruction, you make the d'tors inaccessible to
clients and make them go through a function (a "recycling function?")
to release the instance. The function can then do the two-phase
destruction: call the cleanup method (phase 1) and then destroy the
instance (phase 2).
--
"The Direct3D Graphics Pipeline" -- DirectX 9 draft available for download
<http://legalizeadulthood.wordpress.com/the-direct3d-graphics-pipeline/>

Legalize Adulthood! <http://legalizeadulthood.wordpress.com>
Loading...