Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #11772 > unrolled thread
| Started by | Qi <no@no.no> |
|---|---|
| First post | 2011-11-24 17:20 +0800 |
| Last post | 2011-11-26 11:32 +1300 |
| Articles | 17 — 10 participants |
Back to article view | Back to comp.lang.c++
Any way to detect the absense of virtual destructor in base class? Qi <no@no.no> - 2011-11-24 17:20 +0800
Re: Any way to detect the absense of virtual destructor in base class? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2011-11-24 10:35 +0100
Re: Any way to detect the absense of virtual destructor in base class? Qi <no@no.com> - 2011-11-24 21:05 +0800
Re: Any way to detect the absense of virtual destructor in base class? Juha Nieminen <nospam@thanks.invalid> - 2011-11-25 18:30 +0000
Re: Any way to detect the absense of virtual destructor in base class? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2011-11-25 22:25 +0100
Re: Any way to detect the absense of virtual destructor in base class? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2011-11-25 22:40 +0100
Re: Any way to detect the absense of virtual destructor in base class? Juha Nieminen <nospam@thanks.invalid> - 2011-11-25 22:30 +0000
Re: Any way to detect the absense of virtual destructor in base class? "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2011-11-26 01:36 +0100
Re: Any way to detect the absense of virtual destructor in base class? Juha Nieminen <nospam@thanks.invalid> - 2011-11-26 12:16 +0000
Re: Any way to detect the absense of virtual destructor in base class? Vladimir Jovic <vladaspams@gmail.com> - 2011-11-24 10:45 +0100
Re: Any way to detect the absense of virtual destructor in base class? Qi <no@no.com> - 2011-11-24 21:07 +0800
Re: Any way to detect the absense of virtual destructor in base class? SG <s.gesemann@gmail.com> - 2011-11-24 04:50 -0800
Re: Any way to detect the absense of virtual destructor in base class? Qi <no@no.com> - 2011-11-24 21:04 +0800
Re: Any way to detect the absense of virtual destructor in base class? Marc <marc.glisse@gmail.com> - 2011-11-24 16:12 +0000
Re: Any way to detect the absense of virtual destructor in base class? Lee Steven <steven.mtl@gmail.com> - 2011-11-25 06:55 -0800
Re: Any way to detect the absense of virtual destructor in base class? jgk@panix.com (Joe keane) - 2011-11-25 22:21 +0000
Re: Any way to detect the absense of virtual destructor in base class? Ian Collins <ian-news@hotmail.com> - 2011-11-26 11:32 +1300
| From | Qi <no@no.no> |
|---|---|
| Date | 2011-11-24 17:20 +0800 |
| Subject | Any way to detect the absense of virtual destructor in base class? |
| Message-ID | <jal28n$gpu$1@speranza.aioe.org> |
The full question:
Any portable and standard way to detect the absence of virtual
destructor in base class?
Boost has some type traits for that, but they are intrinsic and rely
on the compilers internal implementation.
Here is some sample code,
class A {
// no virtual destructor
};
class B : public A {
// some data here, whatever
};
A * a = new B;
delete a;
If B has virtual destructor, the behavior of "delete a" is UB. (I
spent half an hour on this issue).
If B has no virtual dtor, memory leak!
So the ideal way is, whenever newing a B like that, a static assert
failure or runtime failure is thrown to indicate A needs a virtual dtor.
Is it possible?
I doubt that, but it's quite annoying to debug that problem in case
I forget to give A a virtual dtor.
If it's impossible, any suggestion on how to avoid forgetting give
base class a virtual dtor?
Thanks
--
WQ
[toc] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2011-11-24 10:35 +0100 |
| Message-ID | <jal34c$2k7$1@dont-email.me> |
| In reply to | #11772 |
On 24.11.2011 10:20, Qi wrote:
> The full question:
> Any portable and standard way to detect the absence of virtual
> destructor in base class?
>
> Boost has some type traits for that, but they are intrinsic and rely
> on the compilers internal implementation.
>
> Here is some sample code,
>
> class A {
> // no virtual destructor
> };
>
> class B : public A {
> // some data here, whatever
> };
>
> A * a = new B;
> delete a;
>
> If B has virtual destructor, the behavior of "delete a" is UB. (I
> spent half an hour on this issue).
> If B has no virtual dtor, memory leak!
>
> So the ideal way is, whenever newing a B like that, a static assert
> failure or runtime failure is thrown to indicate A needs a virtual dtor.
>
> Is it possible?
> I doubt that, but it's quite annoying to debug that problem in case
> I forget to give A a virtual dtor.
>
> If it's impossible, any suggestion on how to avoid forgetting give
> base class a virtual dtor?
Why are you using a raw pointer?
Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`.
These smart pointers remember the proper derived class destruction to
use, freeing you having to have a virtual destructor.
Cheers & hth.,
- Alf
[toc] | [prev] | [next] | [standalone]
| From | Qi <no@no.com> |
|---|---|
| Date | 2011-11-24 21:05 +0800 |
| Message-ID | <jalfnq$lb4$2@speranza.aioe.org> |
| In reply to | #11773 |
On 2011-11-24 17:35, Alf P. Steinbach wrote: > > Why are you using a raw pointer? > > Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`. > > These smart pointers remember the proper derived class destruction to > use, freeing you having to have a virtual destructor. I'm a fan of scoped pointer (though not fan of shared pointer). But raw pointer is still quite useful to me. -- WQ
[toc] | [prev] | [next] | [standalone]
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2011-11-25 18:30 +0000 |
| Message-ID | <4ecfdede$0$4348$7b1e8fa0@news.nbl.fi> |
| In reply to | #11773 |
Alf P. Steinbach <alf.p.steinbach+usenet@gmail.com> wrote:
> Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`.
>
> These smart pointers remember the proper derived class destruction to
> use, freeing you having to have a virtual destructor.
How exactly do they achieve that? If I do, for example, this:
std::unique_ptr<Base*> ptr = new Derived;
then how exactly is 'ptr' able to deduce the derived class destructor in
order to directly call it when it disposes of the object?
(And where would it store it anyways? I thought the whole idea with
std::unique_ptr is that its size is that of one pointer, hence making
it as efficient as a raw pointer.)
I wonder if you are being confused by shared_ptr (and possible other
smart pointers) being able to destroy an object even if they only see
a class declaration (rather than a definition), as long as the class was
fully declared at the point of construction of the pointer. (IOW the class
doesn't need to be fully declared at the place of destruction.) That's not
the same thing as a base-class type smart pointer being able to deduce
the derived-class type destructor.
[toc] | [prev] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2011-11-25 22:25 +0100 |
| Message-ID | <jap151$5an$1@dont-email.me> |
| In reply to | #11823 |
On 25.11.2011 19:30, Juha Nieminen wrote: > Alf P. Steinbach<alf.p.steinbach+usenet@gmail.com> wrote: >> Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`. >> >> These smart pointers remember the proper derived class destruction to >> use, freeing you having to have a virtual destructor. > > How exactly do they achieve that? If I do, for example, this: > > std::unique_ptr<Base*> ptr = new Derived; > > then how exactly is 'ptr' able to deduce the derived class destructor in > order to directly call it when it disposes of the object? > > (And where would it store it anyways? I thought the whole idea with > std::unique_ptr is that its size is that of one pointer, hence making > it as efficient as a raw pointer.) > > I wonder if you are being confused by shared_ptr (and possible other > smart pointers) being able to destroy an object even if they only see > a class declaration (rather than a definition), as long as the class was > fully declared at the point of construction of the pointer. (IOW the class > doesn't need to be fully declared at the place of destruction.) That's not > the same thing as a base-class type smart pointer being able to deduce > the derived-class type destructor. I'm looking at N3290, which I believe is identical to the final standard. And there std::unique_ptr stores a deleter, which can be function object or a function pointer or whatever, specified in constructor call. Essentially it's the same idea as for shared_ptr's deleter except that that unique_ptr doesn't use templating to infer the real type of the object pointer it gets, so one has to write like unique_ptr<Base> p = unique_ptr<Derived>( new Derived ); The only trouble is, I can't get the deleter functionality to work with either Visual C++ 10.0 or g++ 4.4.1 in Windows, nor with g++ 4.6.1 in Ubuntu. That might indicate that my understanding is wrong (in which case the deleter argument then seems worthless), or that unique_ptr is not fully implemented by these compilers, or that N3290 is not identical to C++11? Cheers, & thanks for focusing me on that, - Alf
[toc] | [prev] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2011-11-25 22:40 +0100 |
| Message-ID | <jap21b$b3q$1@dont-email.me> |
| In reply to | #11824 |
On 25.11.2011 22:25, Alf P. Steinbach wrote: > On 25.11.2011 19:30, Juha Nieminen wrote: >> Alf P. Steinbach<alf.p.steinbach+usenet@gmail.com> wrote: >>> Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`. >>> >>> These smart pointers remember the proper derived class destruction to >>> use, freeing you having to have a virtual destructor. >> >> How exactly do they achieve that? If I do, for example, this: >> >> std::unique_ptr<Base*> ptr = new Derived; >> >> then how exactly is 'ptr' able to deduce the derived class destructor in >> order to directly call it when it disposes of the object? >> >> (And where would it store it anyways? I thought the whole idea with >> std::unique_ptr is that its size is that of one pointer, hence making >> it as efficient as a raw pointer.) >> >> I wonder if you are being confused by shared_ptr (and possible other >> smart pointers) being able to destroy an object even if they only see >> a class declaration (rather than a definition), as long as the class was >> fully declared at the point of construction of the pointer. (IOW the >> class >> doesn't need to be fully declared at the place of destruction.) That's >> not >> the same thing as a base-class type smart pointer being able to deduce >> the derived-class type destructor. > > I'm looking at N3290, which I believe is identical to the final standard. > > And there std::unique_ptr stores a deleter, which can be function object > or a function pointer or whatever, specified in constructor call. > > Essentially it's the same idea as for shared_ptr's deleter except that > that unique_ptr doesn't use templating to infer the real type of the > object pointer it gets, so one has to write like > > unique_ptr<Base> p = unique_ptr<Derived>( new Derived ); > > The only trouble is, I can't get the deleter functionality to work with > either Visual C++ 10.0 or g++ 4.4.1 in Windows, nor with g++ 4.6.1 in > Ubuntu. > > That might indicate that my understanding is wrong (in which case the > deleter argument then seems worthless), or that unique_ptr is not fully > implemented by these compilers, or that N3290 is not identical to C++11? I asked it also on SO, http://stackoverflow.com/questions/8274451/well-how-does-the-custom-deleter-of-stdunique-ptr-work Cheers, - Alf
[toc] | [prev] | [next] | [standalone]
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2011-11-25 22:30 +0000 |
| Message-ID | <4ed0171b$0$2769$7b1e8fa0@news.nbl.fi> |
| In reply to | #11824 |
Alf P. Steinbach <alf.p.steinbach+usenet@gmail.com> wrote: > I'm looking at N3290, which I believe is identical to the final standard. > > And there std::unique_ptr stores a deleter, which can be function object > or a function pointer or whatever, specified in constructor call. If std::unique_ptr is indeed specified to store a pointer to a deleter function (or a deleter object), then it's a bit disappointing. When I read that std::unique_ptr would be a better replacement for std::auto_ptr, I was assuming that it's a smart pointer that is as efficient as a raw pointer (at least in terms of size), which destroys the managed object automatically (the advantage over std::auto_ptr being that it's safe to use in data containers such as std::vector, which may move its elements around when reallocating, inserting in the middle or eg. sorting). However, if std::unique_ptr is (at least) the size of *two* pointers, it immediately becomes inferior to a raw pointer in terms of efficiency. While in many/most cases that doesn't matter, it's a bummer in situations where it would. I understand the advantage of having a pointer to a deleter in the smart pointer object: It allows safely deleting an object even in contexts where the class has only been declared (rather than defined), plus some other situations (such as using a specialized deleter for the managed object). However, these situations are quite unusual in practice, and I find it questionable why one has to pay for something that isn't used, which goes against one of the core principles of C++. (After all, this principle is the very reason why eg. classes do not have virtual tables by default, but only if there specifically are virtual functions. You don't have to pay for what you don't use.) Or is std::unique_ptr able to elide storing a deleter pointer (eg. by using some kind of empty base class optimization trick) in cases where no deleter is explicitly specified?
[toc] | [prev] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2011-11-26 01:36 +0100 |
| Message-ID | <japca2$bcp$1@dont-email.me> |
| In reply to | #11827 |
On 25.11.2011 23:30, Juha Nieminen wrote:
> Alf P. Steinbach<alf.p.steinbach+usenet@gmail.com> wrote:
>> I'm looking at N3290, which I believe is identical to the final standard.
>>
>> And there std::unique_ptr stores a deleter, which can be function object
>> or a function pointer or whatever, specified in constructor call.
>
> If std::unique_ptr is indeed specified to store a pointer to a deleter
> function (or a deleter object), then it's a bit disappointing.
>
> When I read that std::unique_ptr would be a better replacement for
> std::auto_ptr, I was assuming that it's a smart pointer that is as
> efficient as a raw pointer (at least in terms of size), which destroys
> the managed object automatically (the advantage over std::auto_ptr being
> that it's safe to use in data containers such as std::vector, which may
> move its elements around when reallocating, inserting in the middle or
> eg. sorting).
>
> However, if std::unique_ptr is (at least) the size of *two* pointers,
> it immediately becomes inferior to a raw pointer in terms of efficiency.
> While in many/most cases that doesn't matter, it's a bummer in situations
> where it would.
Yes, that is the case, sorry.
>
> I understand the advantage of having a pointer to a deleter in the smart
> pointer object: It allows safely deleting an object even in contexts where
> the class has only been declared (rather than defined), plus some other
> situations (such as using a specialized deleter for the managed object).
> However, these situations are quite unusual in practice, and I find it
> questionable why one has to pay for something that isn't used, which goes
> against one of the core principles of C++. (After all, this principle is
> the very reason why eg. classes do not have virtual tables by default,
> but only if there specifically are virtual functions. You don't have to
> pay for what you don't use.)
I agree.
> Or is std::unique_ptr able to elide storing a deleter pointer (eg. by
> using some kind of empty base class optimization trick) in cases where
> no deleter is explicitly specified?
I'm not sure.
Anyway I misunderstood the deleter thing. As someone pointed out on SO,
with unique_ptr the deleter type is a template argument for the /type/.
And it doesn't work (directly) for the usage where you allocate as
derived and delete as base, without virtual destructor.
However, I managed to browbeat the unique_ptr functionality into doing
that, by applying a little type erasure: :-)
<code>
#include <iostream>
#include <memory> // std::unique_ptr
#include <functional> // function
#include <utility> // move
#include <string>
using namespace std;
class Base
{
public:
Base() { cout << "Base:<init>" << endl; }
~Base() { cout << "Base::<destroy>" << endl; }
};
class Derived
: public Base
{
public:
Derived() { cout << "Derived::<init>" << endl; }
~Derived() { cout << "Derived::<destroy>" << endl; }
};
class BoundDeleter
{
private:
typedef void (*DeleteFunc)( void* p );
DeleteFunc deleteFunc_;
void* pObject_;
template< class Type >
static void deleteFuncImpl( void* p )
{
delete static_cast< Type* >( p );
}
public:
template< class Type >
BoundDeleter( Type* pObject )
: deleteFunc_( &deleteFuncImpl< Type > )
, pObject_( pObject )
{}
BoundDeleter( BoundDeleter&& other )
: deleteFunc_( move( other.deleteFunc_ ) )
, pObject_( move( other.pObject_ ) )
{}
void operator() (void*) const
{
deleteFunc_( pObject_ );
}
};
template< class Type >
class SafeCleanupUniquePtr
: public unique_ptr< Type, BoundDeleter >
{
public:
typedef unique_ptr< Type, BoundDeleter > Base;
template< class ActualType >
SafeCleanupUniquePtr( ActualType* p )
: Base( p, BoundDeleter( p ) )
{}
template< class Other >
SafeCleanupUniquePtr( SafeCleanupUniquePtr< Other >&& other )
: Base( move( other ) )
{}
};
int main()
{
SafeCleanupUniquePtr< Base > p( new Derived );
}
</code>
Cheers,
- Alf
[toc] | [prev] | [next] | [standalone]
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2011-11-26 12:16 +0000 |
| Message-ID | <4ed0d898$0$2827$7b1e8fa0@news.nbl.fi> |
| In reply to | #11832 |
Alf P. Steinbach <alf.p.steinbach+usenet@gmail.com> wrote: >> Or is std::unique_ptr able to elide storing a deleter pointer (eg. by >> using some kind of empty base class optimization trick) in cases where >> no deleter is explicitly specified? > > I'm not sure. I did what I should have done earlier, and tested it. In a 64-bit system using gcc 4.6.1 both sizeof(int*) and sizeof(std::unique_ptr<int>) are 8, so I suppose it is smart enough to avoid reserving space for an unneeded deleter pointer. That means I was too hasty to be disappointed with std::unique_ptr.
[toc] | [prev] | [next] | [standalone]
| From | Vladimir Jovic <vladaspams@gmail.com> |
|---|---|
| Date | 2011-11-24 10:45 +0100 |
| Message-ID | <jal3nh$ob6$1@news.albasani.net> |
| In reply to | #11772 |
On 24/11/11 10:20, Qi wrote:
> The full question:
> Any portable and standard way to detect the absence of virtual
> destructor in base class?
>
> Boost has some type traits for that, but they are intrinsic and rely
> on the compilers internal implementation.
>
> Here is some sample code,
>
> class A {
> // no virtual destructor
> };
>
> class B : public A {
> // some data here, whatever
> };
>
> A * a = new B;
> delete a;
>
> If B has virtual destructor, the behavior of "delete a" is UB. (I
> spent half an hour on this issue).
> If B has no virtual dtor, memory leak!
>
> So the ideal way is, whenever newing a B like that, a static assert
> failure or runtime failure is thrown to indicate A needs a virtual dtor.
>
> Is it possible?
> I doubt that, but it's quite annoying to debug that problem in case
> I forget to give A a virtual dtor.
>
> If it's impossible, any suggestion on how to avoid forgetting give
> base class a virtual dtor?
That check would be more complex then adding the "virtual" keyword to
the destructor of the base class.
Anyway, here you are:
#include <boost/type_traits/has_virtual_destructor.hpp>
struct a
{
virtual ~a(){}
};
struct b
{
virtual ~b(){}
};
// ok
static_assert( boost::has_virtual_destructor<a>::value, "no virt
destructor" );
// error
static_assert( boost::has_virtual_destructor<b>::value, "no virt
destructor" );
int main()
{
}
[toc] | [prev] | [next] | [standalone]
| From | Qi <no@no.com> |
|---|---|
| Date | 2011-11-24 21:07 +0800 |
| Message-ID | <jalfqs$lb4$3@speranza.aioe.org> |
| In reply to | #11774 |
On 2011-11-24 17:45, Vladimir Jovic wrote: > // ok > static_assert( boost::has_virtual_destructor<a>::value, "no virt > destructor" ); > // error > static_assert( boost::has_virtual_destructor<b>::value, "no virt > destructor" ); That Boost trait has some assumption on the compiler dependent object binary layout, AFAIK. So it's neither standard nor portable. -- WQ
[toc] | [prev] | [next] | [standalone]
| From | SG <s.gesemann@gmail.com> |
|---|---|
| Date | 2011-11-24 04:50 -0800 |
| Message-ID | <73c0b260-1a76-4de9-a3ed-e272978d916e@y12g2000vba.googlegroups.com> |
| In reply to | #11772 |
On 24 Nov., 10:20, Qi wrote: > The full question: > Any portable and standard way to detect the absence of virtual > destructor in base class? Since you said "standard way" and did not explicitly exclude C++2011: #include <type_traits> ::: std::has_virtual_destructor<YourType>::value Cheers! SG
[toc] | [prev] | [next] | [standalone]
| From | Qi <no@no.com> |
|---|---|
| Date | 2011-11-24 21:04 +0800 |
| Message-ID | <jalfl4$lb4$1@speranza.aioe.org> |
| In reply to | #11778 |
On 2011-11-24 20:50, SG wrote: > Since you said "standard way" and did not explicitly exclude C++2011: > > #include<type_traits> > > ::: > > std::has_virtual_destructor<YourType>::value Thanks This, plus static assert, seems an elegant solution. But, 1, Any equivalence in C++2003 ? 2, If not, any "standard" way to detect the compiler supports only 03 or also supports 11? So I can have some backward compatibility. -- WQ
[toc] | [prev] | [next] | [standalone]
| From | Marc <marc.glisse@gmail.com> |
|---|---|
| Date | 2011-11-24 16:12 +0000 |
| Message-ID | <jalqe2$av4$1@news-rocq.inria.fr> |
| In reply to | #11779 |
Qi wrote: > 2, If not, any "standard" way to detect the compiler > supports only 03 or also supports 11? So I can have some > backward compatibility. The value of the macro __cplusplus *should* do it.
[toc] | [prev] | [next] | [standalone]
| From | Lee Steven <steven.mtl@gmail.com> |
|---|---|
| Date | 2011-11-25 06:55 -0800 |
| Message-ID | <eca72684-4f61-41ae-b40d-91fe4875d5d7@p9g2000vbb.googlegroups.com> |
| In reply to | #11772 |
On Nov 24, 5:20 am, Qi <n...@no.no> wrote:
> The full question:
> Any portable and standard way to detect the absence of virtual
> destructor in base class?
>
> Boost has some type traits for that, but they are intrinsic and rely
> on the compilers internal implementation.
>
> Here is some sample code,
>
> class A {
> // no virtual destructor
>
> };
>
> class B : public A {
> // some data here, whatever
>
> };
>
> A * a = new B;
> delete a;
>
> If B has virtual destructor, the behavior of "delete a" is UB. (I
> spent half an hour on this issue).
> If B has no virtual dtor, memory leak!
>
> So the ideal way is, whenever newing a B like that, a static assert
> failure or runtime failure is thrown to indicate A needs a virtual dtor.
>
> Is it possible?
> I doubt that, but it's quite annoying to debug that problem in case
> I forget to give A a virtual dtor.
>
> If it's impossible, any suggestion on how to avoid forgetting give
> base class a virtual dtor?
>
> Thanks
>
> --
> WQ
Here, I think If A has no virtual dtor, memory leak!
[toc] | [prev] | [next] | [standalone]
| From | jgk@panix.com (Joe keane) |
|---|---|
| Date | 2011-11-25 22:21 +0000 |
| Message-ID | <jap4ci$f3k$1@reader1.panix.com> |
| In reply to | #11772 |
fire them
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2011-11-26 11:32 +1300 |
| Message-ID | <9jajbsF4d0U9@mid.individual.net> |
| In reply to | #11826 |
On 11/26/11 11:21 AM, Joe keane wrote: > fire them Fire who? There isn't a premium charged for quoting context. -- Ian Collins
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c++
csiph-web