virtual destructor Vs virtual method

Discussion in 'C++' started by Calvin Lai, Dec 17, 2003.

  1. Calvin Lai

    Calvin Lai Guest

    Hi all,

    I have a simple question. If I have a ClassA as base class, and ClassB
    derive from it. There is a virtual function foo() in ClassA, and in Class B,
    I defined a function called foo() as well (w/ or w/o declaring it as virtual
    doesn't matter since the virtual is inhered, right?). Both of them have a
    virtual destructor.

    Now, here is the part of the code:

    ClassA::foo()
    {
    cout << "In class A"
    }

    ClassB::foo()
    {
    cout << "In class B"
    }

    ClassA::~ClassA()
    {
    cout << "destroying A"
    }

    ClassB::~ClassB()
    {
    cout << "destroying A"
    }

    When deleting objectB (from ClassB), both destructors are called. However,
    when calling objectB's foo, only the classB's foo is called. Why is that?
    Thanks for all your help.
    Calvin Lai, Dec 17, 2003
    #1
    1. Advertising

  2. Calvin Lai

    jeffc Guest

    "Calvin Lai" <> wrote in message
    news:pA3Eb.72661$...
    > Hi all,
    >
    > I have a simple question. If I have a ClassA as base class, and ClassB
    > derive from it. There is a virtual function foo() in ClassA, and in Class

    B,
    > I defined a function called foo() as well (w/ or w/o declaring it as

    virtual
    > doesn't matter since the virtual is inhered, right?).


    Correct.

    > Both of them have a
    > virtual destructor.
    >
    > Now, here is the part of the code:
    >
    > ClassA::foo()
    > {
    > cout << "In class A"
    > }
    >
    > ClassB::foo()
    > {
    > cout << "In class B"
    > }
    >
    > ClassA::~ClassA()
    > {
    > cout << "destroying A"
    > }
    >
    > ClassB::~ClassB()
    > {
    > cout << "destroying A"
    > }
    >
    > When deleting objectB (from ClassB), both destructors are called. However,
    > when calling objectB's foo, only the classB's foo is called. Why is that?


    Because an object of type ClassB is actually composed of parts of both
    ClassA and ClassB. Let's say in ClassA there were a file, and in ClassB
    there was also a file. In an object of type ClassA, there will be 1 file.
    In an object of type ClassB, there will be 2 files. When you delete an
    object of ClassB, don't you want to make sure that both files are closed?
    The closing of the file in ClassA should logically be closed in the
    destructor for ClassA. On the other hand, functions aren't the same as
    storage. Functions have to do with behavior, and if you want the behavior
    of ClassB to be different from that of ClassA, then there's no reason to
    call ClassA's function. You could though. In ClassB::foo(), you could make
    a call to ClassA::foo(). Then it would work the same way as the destructor
    calls.
    jeffc, Dec 17, 2003
    #2
    1. Advertising

  3. Calvin Lai

    Calvin Lai Guest

    Thanks Jeff. However, is this a logical reasoning of why this is happening
    by design, or there is other behind the door arguments for it?

    Calvin


    "jeffc" <> wrote in message
    news:...
    >
    > "Calvin Lai" <> wrote in message
    > news:pA3Eb.72661$...
    > > Hi all,
    > >
    > > I have a simple question. If I have a ClassA as base class, and ClassB
    > > derive from it. There is a virtual function foo() in ClassA, and in

    Class
    > B,
    > > I defined a function called foo() as well (w/ or w/o declaring it as

    > virtual
    > > doesn't matter since the virtual is inhered, right?).

    >
    > Correct.
    >
    > > Both of them have a
    > > virtual destructor.
    > >
    > > Now, here is the part of the code:
    > >
    > > ClassA::foo()
    > > {
    > > cout << "In class A"
    > > }
    > >
    > > ClassB::foo()
    > > {
    > > cout << "In class B"
    > > }
    > >
    > > ClassA::~ClassA()
    > > {
    > > cout << "destroying A"
    > > }
    > >
    > > ClassB::~ClassB()
    > > {
    > > cout << "destroying A"
    > > }
    > >
    > > When deleting objectB (from ClassB), both destructors are called.

    However,
    > > when calling objectB's foo, only the classB's foo is called. Why is

    that?
    >
    > Because an object of type ClassB is actually composed of parts of both
    > ClassA and ClassB. Let's say in ClassA there were a file, and in ClassB
    > there was also a file. In an object of type ClassA, there will be 1 file.
    > In an object of type ClassB, there will be 2 files. When you delete an
    > object of ClassB, don't you want to make sure that both files are closed?
    > The closing of the file in ClassA should logically be closed in the
    > destructor for ClassA. On the other hand, functions aren't the same as
    > storage. Functions have to do with behavior, and if you want the behavior
    > of ClassB to be different from that of ClassA, then there's no reason to
    > call ClassA's function. You could though. In ClassB::foo(), you could

    make
    > a call to ClassA::foo(). Then it would work the same way as the

    destructor
    > calls.
    >
    >
    Calvin Lai, Dec 17, 2003
    #3
  4. Calvin Lai

    David White Guest

    "Calvin Lai" <> wrote in message
    news:994Eb.72669$...
    > Thanks Jeff. However, is this a logical reasoning of why this is happening
    > by design, or there is other behind the door arguments for it?


    I'm not sure what you mean. A destructor isn't a normal function. It is
    there to do necessary cleanup, so a derived class has no business preventing
    its base class's destructor executing. Therefore, it executes automatically.
    An ordinary member function, OTOH, is a different matter. A derived class is
    there to alter or extend the behaviour of its base class, so it sometimes
    is, and sometimes isn't, appropriate for a virtual function override to call
    its base class version. For example, a virtual Clone function returns a new
    copy of an object. Obviously, only one copy should be made and it should be
    of the most derived class, so it would make no sense at all for any virtual
    Clone function in a class hierarchy to call its base-class version.

    Futhermore, even when a virtual function does call its base class version,
    in some cases it should be called first, in others last, and in still others
    somewhere in the middle of the derived class override. It all depends on
    just how the derived class wants to modify/extend the base class's
    behaviour.

    For all these reasons, calling the base class version of an ordinary virtual
    function from an override is left up to the programmer.

    DW
    David White, Dec 17, 2003
    #4
  5. Calvin Lai

    jeffc Guest

    "Calvin Lai" <> wrote in message
    news:994Eb.72669$...
    > Thanks Jeff. However, is this a logical reasoning of why this is happening
    > by design, or there is other behind the door arguments for it?


    I don't understand your question. If you mean why was the language
    originally designed this way, Bjarne Stroustrup wrote a book about that.
    From an object-oriented design perspective though (which is unrelated to the
    C++ language per se), it makes good sense to me.
    jeffc, Dec 17, 2003
    #5
  6. Calvin Lai

    Kevin Saff Guest

    "Calvin Lai" <> wrote in message
    news:994Eb.72669$...
    > Thanks Jeff. However, is this a logical reasoning of why this is happening
    > by design, or there is other behind the door arguments for it?
    >
    > Calvin


    It's by design. Perhaps the following (succint, yet dumb) code example will
    help.

    /////

    #include <iostream>
    #include <ostream>

    class Base
    {
    public:
    Base()
    {
    std::cout << "Base: " << foo() << std::endl;
    }
    virtual int foo() {return 3;}
    virtual ~Base()
    {
    std::cout << "~Base: " << foo() << std::endl;
    }
    };

    class Derived
    {
    int *x_;
    public:
    Derived() : x_ (new int (5))
    {
    std::cout << "Derived: " << foo() << std::endl;
    }
    int foo() {return *x_;}
    ~Derived()
    {
    std::cout << "~Derived: " << foo() << std::endl;
    delete x_;
    }
    };

    int main()
    {
    Derived d;
    }

    /////

    creates output:
    Base: 3
    Derived: 5
    ~Derived: 5
    ~Base: 3


    If Base could reach Derived::foo in its destructor, it would try to access
    invalid memory. From a design point of view we get much better class
    invariants* if we can assume a function will only be called on a constructed
    object. Otherwise here we might need to (somehow) check in Derived::foo if
    an actual Derived object exists or not - which seems kind of silly.


    *: (Invariants are conditions that are always true for an object. An
    invariant in Derived above is that x_ always points to a valid int, so we
    never need to do the check x_ against NULL. Maintaining good invariants is
    key to OO programming, and a motivating reason for data encapsulation.)

    HTH
    --
    KCS
    Kevin Saff, Dec 17, 2003
    #6
  7. Calvin Lai

    Kevin Saff Guest

    "Kevin Saff" <> wrote in message
    news:...
    > "Calvin Lai" <> wrote in message
    > news:994Eb.72669$...
    > > Thanks Jeff. However, is this a logical reasoning of why this is

    happening
    > > by design, or there is other behind the door arguments for it?
    > >

    > [SNIP]
    >
    >
    > If Base could reach Derived::foo in its destructor, it would try to access
    > invalid memory. From a design point of view we get much better class


    I totally misunderstood the question. How embarrassing! Guess I'm done for
    the day.

    --
    KCS
    Kevin Saff, Dec 17, 2003
    #7
  8. Calvin Lai

    Calvin Lai Guest

    Thanks all for your comments. They are very helpful. I got it now.


    "Calvin Lai" <> wrote in message
    news:994Eb.72669$...
    > Thanks Jeff. However, is this a logical reasoning of why this is happening
    > by design, or there is other behind the door arguments for it?
    >
    > Calvin
    >
    >
    > "jeffc" <> wrote in message
    > news:...
    > >
    > > "Calvin Lai" <> wrote in message
    > > news:pA3Eb.72661$...
    > > > Hi all,
    > > >
    > > > I have a simple question. If I have a ClassA as base class, and ClassB
    > > > derive from it. There is a virtual function foo() in ClassA, and in

    > Class
    > > B,
    > > > I defined a function called foo() as well (w/ or w/o declaring it as

    > > virtual
    > > > doesn't matter since the virtual is inhered, right?).

    > >
    > > Correct.
    > >
    > > > Both of them have a
    > > > virtual destructor.
    > > >
    > > > Now, here is the part of the code:
    > > >
    > > > ClassA::foo()
    > > > {
    > > > cout << "In class A"
    > > > }
    > > >
    > > > ClassB::foo()
    > > > {
    > > > cout << "In class B"
    > > > }
    > > >
    > > > ClassA::~ClassA()
    > > > {
    > > > cout << "destroying A"
    > > > }
    > > >
    > > > ClassB::~ClassB()
    > > > {
    > > > cout << "destroying A"
    > > > }
    > > >
    > > > When deleting objectB (from ClassB), both destructors are called.

    > However,
    > > > when calling objectB's foo, only the classB's foo is called. Why is

    > that?
    > >
    > > Because an object of type ClassB is actually composed of parts of both
    > > ClassA and ClassB. Let's say in ClassA there were a file, and in ClassB
    > > there was also a file. In an object of type ClassA, there will be 1

    file.
    > > In an object of type ClassB, there will be 2 files. When you delete an
    > > object of ClassB, don't you want to make sure that both files are

    closed?
    > > The closing of the file in ClassA should logically be closed in the
    > > destructor for ClassA. On the other hand, functions aren't the same as
    > > storage. Functions have to do with behavior, and if you want the

    behavior
    > > of ClassB to be different from that of ClassA, then there's no reason to
    > > call ClassA's function. You could though. In ClassB::foo(), you could

    > make
    > > a call to ClassA::foo(). Then it would work the same way as the

    > destructor
    > > calls.
    > >
    > >

    >
    >
    Calvin Lai, Dec 18, 2003
    #8
    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. Chunhui Han
    Replies:
    2
    Views:
    500
  2. frs
    Replies:
    20
    Views:
    751
    Alf P. Steinbach
    Sep 21, 2005
  3. arun
    Replies:
    2
    Views:
    541
    benben
    Jun 13, 2006
  4. Jimmy Hartzell
    Replies:
    0
    Views:
    417
    Jimmy Hartzell
    May 19, 2008
  5. Jimmy Hartzell
    Replies:
    2
    Views:
    1,168
    Jimmy Hartzell
    May 20, 2008
Loading...

Share This Page