Re: virtual destruction when?

Discussion in 'C++' started by Andrey Tarasevich, Aug 22, 2003.

  1. John L Fjellstad wrote:
    > I have the following two classes:
    >
    > class Base
    > {
    > public:
    > void addref() { ++refs_; }
    > void release() { if (--refs_ == 0) delete this; }
    >
    > protected:
    > Base() : refs_(0) {}
    > ~Base() {} // virtual or non-virtual?
    >
    > private:
    > int refs_;
    > };
    >
    > class Foo : public Base
    > {
    > ...
    > };
    >
    > Does Base::~Base() need to be virtual if there are no deletion of Foo
    > through a pointer to Base (all deletion is done through Base::release())?


    The fact that all deletions are done through 'Base::release()' doesn't
    necessarily mean that 'this' pointer always points to an object of
    dynamic type 'Base'.

    > Is the this pointer pointing at Base or Foo in the example above?


    I don't see the actual example. In the example below

    Base* p = new Foo;
    p->addref();
    p->release();

    'delete this' inside 'Base::release()' will make an attempt to delete an
    object of type 'Foo' through a pointer of type 'Base*', which means that
    the destructor of 'Base' must be virtual for this code to work correctly.

    --
    Best regards,
    Andrey Tarasevich
    Brainbench C and C++ Programming MVP
     
    Andrey Tarasevich, Aug 22, 2003
    #1
    1. Advertising

  2. John L Fjellstad wrote:
    > ...
    > Sorry, posted too early. The code will always look like this:
    >
    > Foo* p = new Foo;
    > p->addRef();
    > p->release();
    >
    > There will never be a pointer to Base. Reason I can guarantee that is
    > because I'm writing the code:)


    This doesn't change much. Even you don't declare any pointers to base
    yourself, the type of 'this' pointer inside 'Base::release' method is
    'Base*'. Which means that 'delete this' inside the above call to
    'Base::release' will make an attempt to destroy an object of type 'Foo'
    through a pointer of type 'Base*'. You still need a virtual destructor.

    > The basic idea was to have a reference counter Base class, instead of
    > implementing addRef() and release() in all my classes. But I would like to
    > avoid the "penalty" of a virtual function if I can avoid it.


    As long as the actual deletion ('delete this') of 'Foo' object takes
    place in one of the 'Base's methods, you are going to need a virtual
    destructor. Technically, in order to get rid of this requirement you may
    try something like this

    class Base
    {
    public:
    void addref() { ++refs_; }
    bool release() { return --refs_ == 0; }

    protected:
    Base() : refs_(0) {}

    private:
    int refs_;
    };

    class Foo : private Base
    {
    public:
    using Base::addref;
    void release() { if (Base::release()) delete this; }
    };

    Note, that in this case the actual deletion is performed within one of
    the 'Foo's methods (objects of type 'Foo' are destroyed through pointers
    of type 'Foo*'). Virtual destructor is no longer required.

    Note also, that since you are planning to use class 'Base' as a mere
    detail of class 'Foo's implementation, it is better to stick with
    non-public inheritance (I used 'private') or, maybe even better, use
    aggregation instead of inheritance. Public inheritance within OOP
    paradigm is normally used to express IS-A relationship between classes,
    which is not what you are trying to do in this case.

    It is also worth mentioning that the above sample is intended to serve
    illustrational purposes only. It is rather cumbersome, since it requires
    method 'release' to be re-implemented in each concrete class. You can
    easily find much more elegant template-based implementations of
    reference-counting technique that don't require virtual destructors.

    > Basically, I'm unclear of what this points to in the example above, Base or
    > Foo?


    You need to get familiar with notions of 'static type' and 'dynamic
    type' of an object. Inside 'Base::release' the type of 'this' pointer is
    'Base*', which immediately means that the static type of object '*this'
    is 'Base'. However, you know that the actual type of the entire object
    'this' points to is 'Foo' (dynamic type of '*this' is 'Foo'). As you
    see, static type of the object being deleted in 'Base::release' doesn't
    match its dynamic type, meaning that virtual destructor is required.

    'delete this' is moved to 'Foo::release' in my example. Within
    'Foo:release' pointer 'this' has type 'Foo*'. Now both static and
    dynamic types of '*this' are matching. No virtual destructor is required.

    --
    Best regards,
    Andrey Tarasevich
    Brainbench C and C++ Programming MVP
     
    Andrey Tarasevich, Aug 23, 2003
    #2
    1. Advertising

  3. Thanks for taking the time to explain and answer my question. It was really
    informative.

    --
    John L. Fjellstad

    A: Top posting!
    Q: What is the most irritating thing on Usenet?
     
    John L Fjellstad, Aug 25, 2003
    #3
  4. Andrey Tarasevich

    Big Brian Guest

    > Andrey Tarasevich wrote:
    >
    > > I don't see the actual example. In the example below
    > >
    > > Base* p = new Foo;
    > > p->addref();
    > > p->release();

    >
    > Sorry, posted too early. The code will always look like this:
    >
    > Foo* p = new Foo;
    > p->addRef();
    > p->release();
    >


    > There will never be a pointer to Base. Reason I can guarantee that is
    > because I'm writing the code:)


    That's not a good idea. You need to guarantee this by writing your
    code so that COMPILER guarantees it.

    > The basic idea was to have a reference counter Base class, instead of
    > implementing addRef() and release() in all my classes. But I would like to
    > avoid the "penalty" of a virtual function if I can avoid it.
    >
    > Basically, I'm unclear of what this points to in the example above, Base or
    > Foo?


    IMHO, you should rethink what you're tyring to do. Usually calling
    "delete this" is not a good idea. If you're wanting reference
    counting, use a library which implements it correctly, since it's more
    difficult to get right than people first think. But to answer your
    question, "this" in release() points to Base. Although it's probably
    a good idea to make this virtual, in this case as is, the destructor
    doesn't NEED to be virtual because Foo doesn't have any members which
    need to be destroyed, thus calling the base constructor is good
    enough.

    Unless your writing an embedded application or something were
    performance is very critical, worring about the penalty of using
    virtual functions is a bad idea. If you're worried about such things,
    you shouldn't be using inheritance at all. Inheritance implies object
    oriented programming, of which polymorphism is the cornerstone. If
    you're really that worried about the penalty of virtual, maybe you
    should consider writting your application in assembler so you have
    complete control.
     
    Big Brian, Aug 25, 2003
    #4
  5. Big Brian wrote:

    > IMHO, you should rethink what you're tyring to do. Usually calling
    > "delete this" is not a good idea.


    As long as you can guarantee that the object are not called after the
    release (delete this), it should be fine. Only place I actually you
    reference counters like this (addRef() and release() couple), are in the
    pimpl idiom.

    > If you're wanting reference
    > counting, use a library which implements it correctly, since it's more
    > difficult to get right than people first think.


    Well, there is something to be said about handrolling code before you use
    the libraries. For instance, how will I understand the problems with
    reference counter, if I always depend on a library to implement it for me?

    > Unless your writing an embedded application or something were
    > performance is very critical, worring about the penalty of using
    > virtual functions is a bad idea.


    Well, normally I would make all destructor virtual if my class was a base
    class.

    But then I read Alexandrescu's Modern C++ Design, and he noted that
    destructors for policy classes didn't have to, and shouldn't, be virtual,
    as long as you can guarantee that nobody calls delete on the Policy class.

    Reading that made me realize that I had some misunderstanding on why and
    when virtual for destructors were needed. Which is why I came up with the
    contrived example.

    --
    John L. Fjellstad

    A: Top posting!
    Q: What is the most irritating thing on Usenet?
     
    John L Fjellstad, Aug 26, 2003
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ian douglas
    Replies:
    0
    Views:
    1,847
    ian douglas
    Aug 19, 2003
  2. Andrew Koenig

    Re: virtual destruction when?

    Andrew Koenig, Aug 22, 2003, in forum: C++
    Replies:
    1
    Views:
    423
    John Harrison
    Aug 23, 2003
  3. V Patel
    Replies:
    5
    Views:
    321
    Yahooooooooo
    Jan 30, 2007
  4. BeautifulMind
    Replies:
    7
    Views:
    689
    Ron Natalie
    Feb 8, 2007
  5. oliver
    Replies:
    2
    Views:
    250
    red floyd
    Jul 14, 2011
Loading...

Share This Page