virtual functions and destructors (newbie)

P

Peter

I *think* I understand the need for virtual functions. If you call a method
defined in Base, that method has no way of knowing about an overridden
functions in Derived.

Somehow, virtual functions allow methods in Base to "know" about other
functions that are redefined in Derived.



Now I'm trying to understand the need for virtual destructors. In this code:

#include <iostream>
using namepaste std;

class Base
{
public:
Base() { cout << "Constructor: Base" << endl; }
~Base(){ cout << "Destructor : Base" << endl; }
};

class Derived: public Base
{
public:
Derived() { cout << "Constructor: Derived" << endl; }
~Derived(){ cout << "Destructor : Derived" << endl; }
};

int main(void)
{
Base *Var = new Derived();
delete Var;
return 0;
}

the derived destructor is not called, and this is fixed by declaring the base
destructor virtual.

Unfortunately, this is an unsatisfying explanation: I don't understand why
the derived destructor isn't being called. I could certainly have a function
named foo() in both Base and Derived, and if I used:

Var->foo();

I would get Derived::foo(), not Base::foo().

So my first question is, why the difference? It seems more natural for
"delete Var" to call Derived::~Derived, not Base::~Base. After all, Var is
of type pointer to Derived, not pointer to Base. So _why_ is ~Derived() not
called in this case?



Secondly, there seems to be a disconnect between virtual functions and
virtual destructors.

Virtual functions help Base functions know about functions that are redefined
in Derived. In other words, to help old code know about new code.

Virtual destructors seem to help the Derived class know about its own
destructor.

I'm getting a flashback from when I was trying to sort out the many uses for
the word "static". Is there some common logical connection that explains why
we use the word "virtual" to do these seemingly two very different tasks?


Thanks!
Pete
 
V

Victor Bazarov

Peter said:
Now I'm trying to understand the need for virtual destructors. In this code:

#include <iostream>
using namepaste std;

class Base
{
public:
Base() { cout << "Constructor: Base" << endl; }
~Base(){ cout << "Destructor : Base" << endl; }
};

class Derived: public Base
{
public:
Derived() { cout << "Constructor: Derived" << endl; }
~Derived(){ cout << "Destructor : Derived" << endl; }
};

int main(void)
{
Base *Var = new Derived();
delete Var;
return 0;
}

the derived destructor is not called, and this is fixed by declaring the base
destructor virtual.

Unfortunately, this is an unsatisfying explanation: I don't understand why
the derived destructor isn't being called.

Could it be because the destructor is not virtual?
> I could certainly have a function
named foo() in both Base and Derived, and if I used:

Var->foo();

I would get Derived::foo(), not Base::foo().

You would? Really??? Do, please, humour me, try it.
So my first question is, why the difference?

There is none.
> It seems more natural for
"delete Var" to call Derived::~Derived, not Base::~Base. After all, Var is
of type pointer to Derived, not pointer to Base. So _why_ is ~Derived() not
called in this case?

Dare I repeat myself? Because it's not virtual.
Secondly, there seems to be a disconnect between virtual functions and
virtual destructors.
Really???

Virtual functions help Base functions know about functions that are redefined
in Derived.
No.

> In other words, to help old code know about new code.

No, not exactly.
Virtual destructors seem to help the Derived class know about its own
destructor.
Huh?

I'm getting a flashback from when I was trying to sort out the many uses for
the word "static". Is there some common logical connection that explains why
we use the word "virtual" to do these seemingly two very different tasks?

No. 'virtual' has only one overload: inheritance can be virtual and it
means slightly different thing than 'virtual' in functions. 'static' has
_four_ overloads.

V
 
R

Rolf Magnus

Peter said:
int main(void)
{
Base *Var = new Derived();
delete Var;
return 0;
}
[snip]

Unfortunately, this is an unsatisfying explanation: I don't understand why
the derived destructor isn't being called. I could certainly have a
function named foo() in both Base and Derived, and if I used:

Var->foo();

I would get Derived::foo(), not Base::foo().

No, you woldn't. You'd get Base::foo().
So my first question is, why the difference?

There is no difference.
It seems more natural for "delete Var" to call Derived::~Derived, not
Base::~Base. After all, Var is of type pointer to Derived, not pointer to
Base.

Above, you wrote 'Base *Var', so Var is a pointer to Base.
Secondly, there seems to be a disconnect between virtual functions and
virtual destructors.

Virtual functions help Base functions know about functions that are
redefined in Derived. In other words, to help old code know about new
code.

Virtual destructors seem to help the Derived class know about its own
destructor.

A virtual function helps the compiler find out which function to call
without knowing the actual type of the object. Similarly, a virtual
destructor helps finding which destructor to call without knowing the
actual type of the object.
I'm getting a flashback from when I was trying to sort out the many uses
form the word "static".

Static has indeed several different meanings, though with a bit of
abstraction, you can reduce that number to two or so.
 
M

mlimber

Peter said:
I *think* I understand the need for virtual functions. If you call a method
defined in Base, that method has no way of knowing about an overridden
functions in Derived.

Somehow, virtual functions allow methods in Base to "know" about other
functions that are redefined in Derived.

More accurately, virtual functions allow the user to override the
behavior of the base class.

You can also declare abstract classes that have one or more pure
virtual functions, i.e., functions that have no body in the base class
and MUST be defined by a subclass (or a subsubclass, etc.) in order to
be instantiated. This is useful for establishing interfaces. For
instance, assume we have a Document and Clipboard class defined for a
standard text editor. Then we can establish a command processing scheme
like this:

struct Command
{
virtual ~Command() {} // Empty virtual destructor (see below)
virtual void Execute() = 0; // pure virtual
};

struct CopyCommand : public Command
{
CopyCommand( Document& doc, Clipboard& clip )
: doc_(doc), clip_(clip)
{}

virtual void Execute()
{
clip_.SetText( doc_.GetSelection() );
}

protected:
Document& doc_;
Clipboard& clip_;
};

struct CutCommand : public CopyCommand
{
CutCommand( Document& doc, Clipboard& clip )
: CopyCommand( doc, clip )
{}

void Execute()
{
CopyCommand::Execute();
doc_.DeleteSelection();
}
};

struct PasteCommand : public Command
{
CutCommand( Document& doc, Clipboard& clip )
: doc_(doc), clip_(clip)
{}

void Execute()
{
doc_.InsertText( clip_.GetText() );
}

private:
Document& doc_;
Clipboard& clip_;
};

struct MoveCursorCommand : public Command
{
MoveCursorCommand( Document& doc, int lines )
: doc_( doc ), lines_(lines)
{}

void Execute()
{
doc_.MoveCursor( lines_ );
}

private:
Document& doc_;
int lines_;
};

Now we can do a number of things like queue up commands for later
execution:

void CopyAndPasteThrice( Document& doc, Clipboard& clip )
{
std::vector<Cmd*> cmds;
cmds.push_back( new CopyCommand( doc, clip ) );
cmds.push_back( new MoveCursorCommand( doc, 2 ) );
cmds.push_back( new PasteCommand( doc, clip ) );
cmds.push_back( new MoveCursorCommand( doc, 2 ) );
cmds.push_back( new PasteCommand( doc, clip ) );
cmds.push_back( new MoveCursorCommand( doc, 2 ) );
cmds.push_back( new PasteCommand( doc, clip ) );

// ...

// Execute our command queue
typedef std::vector::iterator it;
for( it cmd = cmds.begin(); cmd != cmds.end(); ++cmd )
{
cmd->Execute();
}

// ... Don't forget to delete the commands
}

Notice that we can use all commands with the same interface (namely,
the Execute function) without knowing the specific derived type of the
object. We can also delete them uniformly since the destructor of
Command is also virtual. The key is that we know only that each is a
Command and can Execute, not what the command specifically does.
Now I'm trying to understand the need for virtual destructors. In this code:

#include <iostream>
using namepaste std;

class Base
{
public:
Base() { cout << "Constructor: Base" << endl; }
~Base(){ cout << "Destructor : Base" << endl; }
};

class Derived: public Base
{
public:
Derived() { cout << "Constructor: Derived" << endl; }
~Derived(){ cout << "Destructor : Derived" << endl; }
};

int main(void)
{
Base *Var = new Derived();
delete Var;
return 0;
}

the derived destructor is not called, and this is fixed by declaring the base
destructor virtual.

Unfortunately, this is an unsatisfying explanation: I don't understand why
the derived destructor isn't being called. I could certainly have a function
named foo() in both Base and Derived, and if I used:

Var->foo();

I would get Derived::foo(), not Base::foo().

Only if foo were declared virtual. If it's not, you'd get Base::foo.
So my first question is, why the difference? It seems more natural for
"delete Var" to call Derived::~Derived, not Base::~Base. After all, Var is
of type pointer to Derived, not pointer to Base. So _why_ is ~Derived() not
called in this case?

Consider:

struct Base
{
virtual ~Base();
virtual void Foo();
void Bar();
};

struct Derived : public Base
{
~Derived();
void Foo();
void Bar(); // hides Base::Bar()
};

If a member function is not virtual, normal function calls take place:

void f( Base* b )
{
b->Foo(); // Calls Base::Foo() if b points to a Base object
// or Derived::Foo() if b points to a Derived object

b->Bar(); // Calls Base::Bar() even if b points to a Derived object

delete b; // Calls Base::~Base() if b points to a Base object
// or Derived::~Derived() if b points to a Derived object
}

Essentially what happens is that a table of virtual functions is
established in every object with a base class of type Base. The table
holds the address of the virtual (and only virtual) functions for the
current object. If the object is of type Base, then the virtual table
holds the address of Base::Foo. If the object is of type Derived, then
the table holds the address of Derived::Foo. If the function is
virtual, the correct address is looked up in the table in order to make
the call (hence the [spurious] complaint that virtual calls drastically
slow programs down because of an additional indirection).

The same is true of the destructor, and therefore you should usually
write (possibly empty) virtual destructors in classes that have virtual
functions. See this FAQ:

http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7
Secondly, there seems to be a disconnect between virtual functions and
virtual destructors.

Virtual functions help Base functions know about functions that are redefined
in Derived. In other words, to help old code know about new code.

Virtual destructors seem to help the Derived class know about its own
destructor.

I'm getting a flashback from when I was trying to sort out the many uses for
the word "static". Is there some common logical connection that explains why
we use the word "virtual" to do these seemingly two very different tasks?

No, the use of the virtual keyword is uniform between the two.

Cheers! --M
 
P

Peter_Julian

| I *think* I understand the need for virtual functions. If you call a
method
| defined in Base, that method has no way of knowing about an overridden
| functions in Derived.
|
| Somehow, virtual functions allow methods in Base to "know" about other
| functions that are redefined in Derived.

The "somehow" is better explained when you consider that a class or
struct with at least one virtual function implies a virtual table. The
vtable is created at compile time and populated at runtime.

The vtable essentially lets the instance itself track or know whether
its a base or derived instance in a class hierarchy.

|
| Now I'm trying to understand the need for virtual destructors. In
this code:
|
<snip>
|
| the derived destructor is not called, and this is fixed by declaring
the base
| destructor virtual.

The reason the non-virtual derived d~tor is not called is because no
vtable exists in the class hierarchy. A virtual d~tor implies a
populated vtable.

|
| Unfortunately, this is an unsatisfying explanation: I don't understand
why
| the derived destructor isn't being called. I could certainly have a
function
| named foo() in both Base and Derived, and if I used:
|
| Var->foo();
|
| I would get Derived::foo(), not Base::foo().

No, you'ld get Base::foo() unless you declared Base::foo() as virtual.
The same applies to d~tors. Think vtable again.

|
| So my first question is, why the difference? It seems more natural
for
| "delete Var" to call Derived::~Derived, not Base::~Base. After all,
Var is
| of type pointer to Derived, not pointer to Base. So _why_ is
~Derived() not
| called in this case?

What? Var is a pointer to Base. And ~Derived can't be invoked since the
base's d~tor is not virtual. In other words, Var has no mechanism that
allows it to determine whether its a base or derived object.

|
| Secondly, there seems to be a disconnect between virtual functions and
| virtual destructors.

No, that's incorrect. Just because you have a pointer to a Base element
does not mean you have a pure Base object. As an example: Var in your
code could be either a Base or any derivation thereof. Without the
virtual mechanism, the instance at Var has no way to determine what type
of object it actually is (base or derived).

|
| Virtual functions help Base functions know about functions that are
redefined
| in Derived. In other words, to help old code know about new code.

I disagree, its not "old code". Rather, it's "pertinent" code. A duck, a
pigeon and a chicken all talk in their own way. Yet all these are birds
and all of them can talk. Each type of bird talks in their own distinct
way. Imagine a cage of 3 birds, one of each type. Instead of having the
cage make the birds talk, you are basicly telling each bird to talk in
whatever way they can. Its the bird, not the pointers, that do the
talking. Thats the paradigm shift.

|
| Virtual destructors seem to help the Derived class know about its own
| destructor.
|
| I'm getting a flashback from when I was trying to sort out the many
uses for
| the word "static". Is there some common logical connection that
explains why
| we use the word "virtual" to do these seemingly two very different
tasks?

Static is an entirely seperate issue.
The keyword virtual is how C++ provides the instance of some
type-hierachy to know whether its a base class, a derived class or a
grandchild(etc...). More precisely, virtual generates the vtable which
artificially allows an object, or instance, to be self-aware and
self-destroyable.

And the rule is: if you are planning to use base pointers to store
instances of a given class hierarchy, you need virtual destructors. In
other words, all the d~tors below are automatically virtual to satisfy
that requirement. This is so even though only the base d~tor ~A() is
declared as virtual.

A
{
A() { }
virtual ~A() { }
};

B : public A
{
B() { }
~B() { } // virtual
};

C : public B
{
C() { }
~C() { } // virtual
};

int main()
{
A* p = new C;
delete p; // its the instance at p that knows what type
// it actually is.
// The pointer p doesn't care nor need to know.
}

sequence:
A()
B()
C()
....
~C() // all d~tors are virtual
~B()
~A()
 
S

saikishore.vanga

We know the use of virtual functions. Its main use is to implementing
the overriding concept. That is Functions that are defined in the Base
class, Then we redefined in the derived class.
If we are using virtual keyword in the Base class.

Then it will create one virtual table related to Base class.

But later how it points to either Base class or derived Class.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top