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 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)
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();
}
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
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)
"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
"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
> 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
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
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)
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
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)
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.
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::
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.
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) This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics |
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...
|
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...
|
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...
|
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...
|
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...
| |
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...
|
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...
|
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...
|
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...
|
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...
|
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,...
| |
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...
|
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,...
|
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...
|
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,...
|
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...
|
by: adsilva |
last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
| |
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 ...
|
by: muto222 |
last post by:
How can i add a mobile payment intergratation into php mysql website.
| |