473,498 Members | 1,819 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Proposed work-around for missing virtual destructors

I posted to my blog a special pointer class work-around for inheriting from
base classes without virtual destructors. I was wondering if there is any
other similar work, and whether there are any problems with my proposed
approach. Thanks in advance!
See http://www.artima.com/weblogs/viewpo...?thread=107587

For those who just want the code:

template<typename target_type>
class base_class_ptr {
public:
// forward declarations
template <class T>
struct functions;
template <class T>
base_class_ptr(T* x)
: m_a(x), m_t(&functions<T>::table)
{}
target_type* operator->() {
return static_cast<target_type*>(m_a);
}
void Delete() {
m_t->Delete(m_a);
m_a = NULL;
}
// Function table type
struct table {
void (*Delete)(void*);
};
// For a given referenced type T, generates functions for the
// function table and a static instance of the table.
template<class T>
struct functions
{
static typename base_class_ptr<target_type>::table table;
static void Delete(void* p) {
delete static_cast<T*>(p);
}
};
private:
void* m_a;
table* m_t;
};

template<typename target_T>
template<class T>
typename base_class_ptr<target_T>::table
base_class_ptr<target_T>::functions<T>::table = {
&base_class_ptr<target_T>::template functions<T>::Delete
};

--
Christopher Diggins
Object Oriented Template Library (OOTL)
http://www.ootl.org
Jul 23 '05 #1
15 2090
christopher diggins wrote:
I posted to my blog a special pointer class work-around for inheriting from
base classes without virtual destructors. I was wondering if there is any
other similar work, and whether there are any problems with my proposed
approach.


Too many void*'s. The result is that this isn't typesafe:

struct T {};
struct U {};

base_class_ptr<T>( (U*)0 );

The code doesn't enforce the implied requirement that U is derived from
T. That can be fixed by storing the pointer as a T* instead of a void*,
as is (typically) done in TR1's shared_ptr.

I'd also give this thread a different title: "Evading Design Decisions:
Deleting Types Derived from Classes Without Virtual Destructors". After
all, there's usually a good reason for not providing a virtual destructor.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 23 '05 #2
christopher diggins wrote:
I posted to my blog a special pointer class work-around for inheriting from base classes without virtual destructors. I was wondering if there is any other similar work, and whether there are any problems with my proposed approach. Thanks in advance!
See http://www.artima.com/weblogs/viewpo...?thread=107587

For those who just want the code:

template<typename target_type>
class base_class_ptr {
public:
// forward declarations
template <class T>
struct functions;
template <class T>
base_class_ptr(T* x)
: m_a(x), m_t(&functions<T>::table)
{}
target_type* operator->() {
return static_cast<target_type*>(m_a);
}
void Delete() {
m_t->Delete(m_a);
m_a = NULL;
}
// Function table type
struct table {
void (*Delete)(void*);
};
// For a given referenced type T, generates functions for the
// function table and a static instance of the table.
template<class T>
struct functions
{
static typename base_class_ptr<target_type>::table table;
static void Delete(void* p) {
delete static_cast<T*>(p);
}
};
private:
void* m_a;
table* m_t;
};

template<typename target_T>
template<class T>
typename base_class_ptr<target_T>::table
base_class_ptr<target_T>::functions<T>::table = {
&base_class_ptr<target_T>::template functions<T>::Delete
};

--
Christopher Diggins
Object Oriented Template Library (OOTL)
http://www.ootl.org

I would avoid using void.
The following class is a safer workaround method, and it's type safe.

template<typename base_type>
class base_class_ptr
{
class BaseContainer{
public:
virtual ~BaseContainer(){}
};
template<typename derive_type>
class Container : public BaseContainer{
derive_type *m_derive_ptr;
public:
Container(derive_type* derive_obj):m_derive_ptr(derive_obj){}
~Container(){delete m_derive_ptr;}
};
public:
template<typename derive_type>
base_class_ptr(derive_type* derive_obj)
:m_base_ptr(derive_obj), m_BaseContainer(new
Container<derive_type>(derive_obj)){}
~base_class_ptr(){delete m_BaseContainer;}
base_type* operator->() {return m_base_ptr;}
private:
base_type *m_base_ptr;
BaseContainer *m_BaseContainer;
};

Example usage:
void SomeFunction()
{
base_class_ptr<MyBaseClass> ptr_a(new MyDerivedClass_a);
ptr_a->TestFunct();
base_class_ptr<MyBaseClass> ptr_b(new MyDerivedClass_b);
ptr_b->TestFunct();
}

Jul 23 '05 #3
christopher diggins wrote:
It is commonly recommended in C++ to publicly inherit from
classes which have virtual destructors, to avoid possible memory
leaks.
Not memory leaks but memory corruption. Undefined behavior in general.
I posted to my blog a special pointer class work-around for inheriting from base classes without virtual destructors. I was wondering if there is any other similar work, and whether there are any problems with my proposed approach. Thanks in advance!
See http://www.artima.com/weblogs/viewpo...?thread=107587

For those who just want the code:

template<typename target_type>
class base_class_ptr {
[template fuss snipped]
void* m_a;
table* m_t;
};

template<typename target_T>
template<class T>
typename base_class_ptr<target_T>::table
base_class_ptr<target_T>::functions<T>::table = {
&base_class_ptr<target_T>::template functions<T>::Delete
};


You replace the built-in vtable with you own external, hand-written,
'complicated' vtable. For what reason? Certainly not performance.

Abe

Jul 23 '05 #4
Abecedarian wrote:
christopher diggins wrote:
It is commonly recommended in C++ to publicly inherit from
classes which have virtual destructors, to avoid possible memory
leaks.

Not memory leaks but memory corruption. Undefined behavior in general.


Right. And the true rule is that you should not delete an object of a
derived type through a pointer to a base that does not have a virtual
destructor.

You replace the built-in vtable with you own external, hand-written,
'complicated' vtable. For what reason? Certainly not performance.


No, what he's doing is remembering the derived type, and providing a
function that casts the stored pointer to a pointer to that type in
order to delete it. That's okay, if it's done right.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 23 '05 #5
"Abecedarian" <ab*********@spambob.com> wrote in message
You replace the built-in vtable with you own external, hand-written,
'complicated' vtable. For what reason? Certainly not performance.


Introducing a vtable into an object does hurt performance (whether this
effect is negligble or not depends on your application). It also increase
the amount of memory needed to represent the object. The proposed technique
has the trade-off of only making the pointer bigger.

Christopher Diggins

Jul 23 '05 #6
"Pete Becker" <pe********@acm.org> wrote in message
news:t8********************@giganews.com...
christopher diggins wrote:
I posted to my blog a special pointer class work-around for inheriting
from base classes without virtual destructors. I was wondering if there
is any other similar work, and whether there are any problems with my
proposed approach.


Too many void*'s. The result is that this isn't typesafe:

struct T {};
struct U {};

base_class_ptr<T>( (U*)0 );

The code doesn't enforce the implied requirement that U is derived from T.
That can be fixed by storing the pointer as a T* instead of a void*, as is
(typically) done in TR1's shared_ptr.

I'd also give this thread a different title: "Evading Design Decisions:
Deleting Types Derived from Classes Without Virtual Destructors". After
all, there's usually a good reason for not providing a virtual destructor.


Thanks for pointing these out to me.

- Christopher
Jul 23 '05 #7
> I would avoid using void.
The following class is a safer workaround method, and it's type safe.

template<typename base_type>
class base_class_ptr
{
class BaseContainer{
public:
virtual ~BaseContainer(){}
};
template<typename derive_type>
class Container : public BaseContainer{
derive_type *m_derive_ptr;
public:
Container(derive_type* derive_obj):m_derive_ptr(derive_obj){}
~Container(){delete m_derive_ptr;}
};
public:
template<typename derive_type>
base_class_ptr(derive_type* derive_obj)
:m_base_ptr(derive_obj), m_BaseContainer(new
Container<derive_type>(derive_obj)){}
~base_class_ptr(){delete m_BaseContainer;}
base_type* operator->() {return m_base_ptr;}
private:
base_type *m_base_ptr;
BaseContainer *m_BaseContainer;
};

Example usage:
void SomeFunction()
{
base_class_ptr<MyBaseClass> ptr_a(new MyDerivedClass_a);
ptr_a->TestFunct();
base_class_ptr<MyBaseClass> ptr_b(new MyDerivedClass_b);
ptr_b->TestFunct();
}


This appears to be a cleaner and safer alternative to what I posted, the
only disadvantage I see is the fact that it requires an extra allocation,
which in many cases is not a big deal. Perhaps a solution using placement
new would be even more effective? Anyone up for that challenge?

--
Christopher Diggins
Object Oriented Template Library (OOTL)
http://www.ootl.org
Jul 23 '05 #8
Pete Becker wrote:
Abecedarian wrote:

You replace the built-in vtable with you own external, hand-written, 'complicated' vtable. For what reason? Certainly not performance.


No, what he's doing is remembering the derived type, and providing a
function that casts the stored pointer to a pointer to that type in
order to delete it. That's okay, if it's done right.


Is
m_t->Delete(m_a);

really faster than something like:
(*vtbl[n])(this);

? (ok, one pointer arithmetic less, but ...)

Abe

Jul 23 '05 #9
Abecedarian wrote:

Is
m_t->Delete(m_a);

really faster than something like:
(*vtbl[n])(this);

? (ok, one pointer arithmetic less, but ...)


I'm not sure what you're getting at. m_t->Delete(m_a) calls a template
function. No vtable involved.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 23 '05 #10
Pete Becker wrote:
Abecedarian wrote:

Is
m_t->Delete(m_a);

really faster than something like:
(*vtbl[n])(this);

? (ok, one pointer arithmetic less, but ...)

I'm not sure what you're getting at. m_t->Delete(m_a) calls a

template function. No vtable involved.


AFAICS, he calls a Delete function through a function pointer.
Confusingly this is done by an object of type 'table'.
WRT performance: C++ compilers usually don't inline function pointers
(AFAIK). But they know and optimize vtables and vtable-calls. There was
an article in the last(?) CUJ (the one with your article) about vitual
function call optimizations. Probably the Diggins solution is even
slower with most current compilers than the 'overhead' of a virtual
function call. But, of course, any performance claim without
substantial measurement is useless.

Abe

Jul 23 '05 #11
Abecedarian wrote:

AFAICS, he calls a Delete function through a function pointer.
Confusingly this is done by an object of type 'table'.
WRT performance: C++ compilers usually don't inline function pointers
(AFAIK). But they know and optimize vtables and vtable-calls.
Okay.
Probably the Diggins solution is even
slower with most current compilers than the 'overhead' of a virtual
function call. But, of course, any performance claim without
substantial measurement is useless.


It doesn't seem likely that deleting objects would be a significant
application bottleneck.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 23 '05 #12


Pete Becker wrote:


It doesn't seem likely that deleting objects would be a significant
application bottleneck.


In relation to the amount of time it takes the runtime to coalesce the
freeblock chain it wouldn't be significant at all.

Jul 23 '05 #13
lilburne wrote:
Pete Becker wrote:
It doesn't seem likely that deleting objects would be a significant application bottleneck.
In relation to the amount of time it takes the runtime to coalesce

the freeblock chain it wouldn't be significant at all.


So, the assertion that a vitrual destructor hurts performance is plain
wrong.

::A::

Jul 23 '05 #14


Abecedarian wrote:
lilburne wrote:
Pete Becker wrote:
It doesn't seem likely that deleting objects would be a significant
application bottleneck.


In relation to the amount of time it takes the runtime to coalesce


the
freeblock chain it wouldn't be significant at all.

So, the assertion that a vitrual destructor hurts performance is plain
wrong.


If the object is in the heap rather than the stack your main performance
hit will be in the freeblock trundling.

I have an application that creates 10,000,000+ objects at a time,
deleting the objects could take 20+ minutes as the VC6 runtime free code
trundles about coalescing the freeblock chain. We resolved the problem
by block allocation and using placement new, so we only have to delete a
few 1000 large blocks rather than 10,000,000+ small blocks. The cleaning
up takes a second or so. In our experience it is operator new that is
expensive as it eventually results in free().

A virtual destructor is going to be more work to do so it will be slower
by a few clock cycles, whether it is significant for your application is
another matter. For us it is not.

Jul 23 '05 #15
Abecedarian wrote:

So, the assertion that a vitrual destructor hurts performance is plain
wrong.


It depends on the meaning of "performance." It can make objects bigger.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Jul 23 '05 #16

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
2274
by: Josiah Carlson | last post by:
Good day everyone, I have produced a patch against the latest CVS to add support for two new formatting characters in the struct module. It is currently an RFE, which I include a link to at the...
29
2550
by: C A Upsdell | last post by:
Comments re these three related, proposed CSS 3 additions? 1. Add 'text-transform:time', which would transform text in the format hours:minutes:seconds (24 hour clock) to the local format for...
16
2665
by: G Matthew J | last post by:
http://htmatters.net/htm/1/2005/07/evaling-JSON.cfm This is more or less in response to Mr Crockford's admonition a few months ago, "fork if you must". Ironically, although in that usenet post...
108
6292
by: Bryan Olson | last post by:
The Python slice type has one method 'indices', and reportedly: This method takes a single integer argument /length/ and computes information about the extended slice that the slice object would...
2
1954
by: Richard Cornford | last post by:
Anyone who has taken a look at the online FAQ today may have noticed that I have updated it. The majority of the changes are the updating of broken links and the implementation of that extensive...
6
1441
by: Paul Rubin | last post by:
I'm thinking of proposing that a values method be added to set objects, analogously with dicts. If x is a set, x.values() would be the same as list(x). This feels logical, and it would allow...
31
2375
by: Michael Tobis | last post by:
"Is this the right room for an argument?" http://geosci.uchicago.edu/~tobis/snake.png ok, so my execution is pretty crude, but you can at least see my idea. I trust you to imagine it...
3
2099
by: James J. Besemer | last post by:
I would like to champion a proposed enhancement to Python. I describe the basic idea below, in order to gage community interest. Right now, it's only an idea, and I'm sure there's room for...
5
1725
by: Randy Webb | last post by:
The list below has the current section number, proposed anchor name, and then the current title of that section. Some make sense, some don't. There are a few that don't have proposed anchor names...
0
1068
by: =?Utf-8?B?VEhhZ2d5?= | last post by:
Hello, a running application was transfered to another server an now brings above error message. .NET Framwork Version 2.0.50727.42, ASP.NET Version 2.0.50727.210 Backround: I create a...
0
7126
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
7005
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
7168
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
7210
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
5465
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
3096
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
3087
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
0
1424
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
1
659
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.