Virtual destructor

P

pmizzi

When i compile my program with the -ansi -Wall -pedantic
flags,
i get this warning: `class vechile' has virtual functions
but non-virtual destructor, and the same with my
sub-classes. But when i add a virtual destructor like this :

" virtual ~vechile". I get this error:
Undefined first referenced
symbol in file
vtable for vechile /var/tmp//ccC9yD6Z.o
ld: fatal: Symbol referencing errors. No output written to
a.out
collect2: ld returned 1 exit status


Can anyone help me?
 
I

Ian

pmizzi said:
When i compile my program with the -ansi -Wall -pedantic
flags,
i get this warning: `class vechile' has virtual functions
but non-virtual destructor, and the same with my
sub-classes. But when i add a virtual destructor like this :

" virtual ~vechile". I get this error:

You haven't provides a body for the destructor. If it does nothing ,
then ~vechile() {} will suffice.

Ian
 
E

E. Robert Tisdale

pmizzi said:
When I compile my program with the -ansi -Wall -pedantic flags,
I get this warning: `class vechile' has virtual functions
but non-virtual destructor,

It's a warning. Ignore it.
and the same with my sub-classes.
But, when I add a virtual destructor like this:

"virtual ~vechile", I get this error:

Undefined first referenced
symbol in file
vtable for vechile /var/tmp//ccC9yD6Z.o
ld: fatal: Symbol referencing errors. No output written to
a.out
collect2: ld returned 1 exit status

Can anyone help me?

Don't define a virtual destructor if you don't need one.
If you really need a virtual destructor,
you will need virtual constructors as well.
No function should ever delete an object
that was allocated by the calling function and,
in C++, it is poor programming practice
to require the calling program
to explicitly delete an object through a pointer
returned by a function that allocated the object.
 
?

=?iso-8859-1?Q?Ali_=C7ehreli?=

E. Robert Tisdale said:
It's a warning. Ignore it.

It is a warning that should not be ignored in almost all cases. A virtualy
destructor is only rarely unnecessary. Even then, not defining virtual would
be a timebomb.

Deleting a polymorphic object through a base class pointer is very common.
It is undefined behavior when the destructor is non-virtual in that case.
Don't define a virtual destructor if you don't need one.

That is very rarely the case.
If you really need a virtual destructor,
you will need virtual constructors as well.

Not at all. Using a function to make the decision on what object to
construct and to return a base object pointer is very common:

Base * read_object(/* ... */)
{
/* ... */
return some_condition
? new Derived1
: new Derived2;
}

/* ... */

Base * base = read_object(/* ... */);
No function should ever delete an object
that was allocated by the calling function

I can think of a linked-list implementation that has a function, which
removes objects. Surely the objects would not be allocated in that function,
yet deleted in there.
and,
in C++, it is poor programming practice
to require the calling program
to explicitly delete an object through a pointer
returned by a function that allocated the object.

See the read_object function above. How do you propose we delete the
returned object? Of course approach is to return a suitable smart pointer
that communicates precisely the transfer of ownership, which in turn means
that the object will be deleted outside of the allocating function.

Ali
 
E

E. Robert Tisdale

Ali said:
Not at all. Using a function to make the decision on what object to
construct and to return a base object pointer is very common:

Base* read_object(/* ... */) {
/* ... */
return some_condition? new Derived1: new Derived2;
}

/* ... */

Base* base = read_object(/* ... */);

But it isn't good programming practice in C++.
Your program already knows about types Derived1 and Derived2
so you might as well use them in the calling program.

A virtual constructor is more "extensible".
See Bjarne Stroustrup, "The C++ Programming Language: Third Edition",
Chapter 15: Class Hierarchies, Section 6: Free Store,
Subsection 2: Virtual constructors, pages 424-5.
 
?

=?iso-8859-1?Q?Ali_=C7ehreli?=

E. Robert Tisdale said:
But it isn't good programming practice in C++.

Himmm... I don't see what you mean.

Even Stroustrup refers to such a use in C++ in the pages you quoted. My
read_object does exactly the same thing as his Ival_maker::dial does. Maybe
naming it as "factory method" might
Your program already knows about types Derived1 and Derived2
so you might as well use them in the calling program.

My program knows of them, but the translation unit does not know of them. We
don't always mention the physical structure of code in our posts, but you
can assume that the declaration is

Base * read_object(/* ... */);

and the user is

Base * base = read_object(/* ... */);

So, Derived1 and Derived2 are know only in another translation unit, where
read_object is defined.
A virtual constructor is more "extensible".

It is not more extensible than a function like read_object; you can extend
read_object any way we like. Besides, their application are very different:
read_object returns a brand new object, virtual constructor copies an
existing object. That's why I use virtual constructor in some of my copy
constructors.
See Bjarne Stroustrup, "The C++ Programming Language: Third Edition",
Chapter 15: Class Hierarchies, Section 6: Free Store,
Subsection 2: Virtual constructors, pages 424-5.

Thanks for the refence; I've re-read it. I think he could declare new_expr
as a static member function. Then it could be used without an existing
object; esentially using the same principle as a simple function as
read_object above.

Ali
 
E

E. Robert Tisdale

Ali said:
It is not more extensible than a function like read_object;
you can extend read_object any way we like.
Besides, their application are very different:
read_object returns a brand new object,
virtual constructor copies an existing object.
That's why I use virtual constructor in some of my copy constructors.

Stoustrup's example shows a virtual "default constructor" (new_expr)
and a virtual "copy constructor" (clone) corresponding to
the actual constructors for class Expr.
Thanks for the refence; I've re-read it.
I think he could declare new_expr as a static member function.
Then it could be used without an existing object;
esentially using the same principle
as a simple function as read_object above.

That means that the base class would need to know about
all of the derived types which are, generally, unknown
when the base class is implemented.
 
D

Dave Rahardja

It's a warning. Ignore it.

Famous last words. Warnings should be dealt with, or at least documented,
because they indicate an error-prone construct.

Destructors should almost always be declared virtual. Without virtual
destructors derived classes stand a good chance of not getting destroyed
correctly.
If you really need a virtual destructor,
you will need virtual constructors as well.

Virtual destructors and "virtual constructors" (vis-a-vis Stroustroup) solve
very different problems. Don't confuse them with each other.
No function should ever delete an object
that was allocated by the calling function and,
in C++, it is poor programming practice
to require the calling program
to explicitly delete an object through a pointer
returned by a function that allocated the object.

Nonsense. std::auto_ptr uses that idiom.

There are situations where an Orchestrator may construct an object on the heap
derived from some base class, then passes a reference to that object on the
heap to a helper object as a reference to the base class. The helper object
then "owns" and manages that object, and will destruct it on its own schedule.

There's nothing wrong with that pattern.

-dr
 
E

E. Robert Tisdale

Dave said:
Famous last words. Warnings should be dealt with, or at least documented,
because they indicate an error-prone construct.

Not necessarily. Like lint:

http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?query=LINT&action=Search

most modern C++ compilers will warn you
about perfectly acceptable constructs
that might contain errors:
> cat f.cc
void f(void) {
int a[2][2] = { 0, 1, 2, 3 };
}
> g++ -Wall -ansi -pedantic -c f.cc
f.cc: In function ‘void f()’:
f.cc:2: warning: missing braces around initializer
f.cc:2: warning: unused variable ‘a’
Destructors should almost always be declared virtual.
Without virtual destructors,
derived classes stand a good chance of not getting destroyed correctly.
How?


Virtual destructors and "virtual constructors" (vis-a-vis Stroustroup) solve
very different problems. Don't confuse them with each other.


Nonsense. std::auto_ptr uses that idiom.

There are situations where an Orchestrator may construct an object on the heap
derived from some base class, then passes a reference to that object on the
heap to a helper object as a reference to the base class. The helper object
then "owns" and manages that object, and will destruct it on its own schedule.

Please show us an example.
There's nothing wrong with that pattern.

Except that it is error prone.
 
R

Richard Herring

E. Robert Tisdale said:
But it isn't good programming practice in C++.
Your program already knows about types Derived1 and Derived2
so you might as well use them in the calling program.

So virtual functions are a waste of time. You heard it here first,
folks!
 
R

Richard Herring

E. Robert Tisdale said:
It's a warning. Ignore it.


Don't define a virtual destructor if you don't need one.
If you really need a virtual destructor,
you will need virtual constructors as well.

Nonsense. They aren't paired requirements.
No function should ever delete an object
that was allocated by the calling function

ITYM "programs using pointers need a consistent, well-defined ownership
model"
and,
in C++, it is poor programming practice
to require the calling program
to explicitly delete an object through a pointer
returned by a function that allocated the object.

If the caller doesn't delete the object, what does, and how would it
obviate the need for a virtual dtor?

You've just condemned many kinds of smart pointer, garbage collection,
factory patterns, and much more, as "poor programming practice."

Personally I shall continue to use them whenever it's appropriate.
 
E

E. Robert Tisdale

Richard said:
Personally I shall continue to use them whenever it's appropriate.

Unfortunately, you will continue to use them
whether they are appropriate or not
because you don't know when they are appropriate
and when they are not appropriate.
 
M

Marcus Kwok

E. Robert Tisdale said:

How about:


#include <iostream>

class Base
{
public:
Base() : base_data(new int[5]) { std::cout << "B()\n"; }
~Base() { std::cout << "~B()\n"; delete [] base_data; }
protected:
int* base_data;
};

class Derived : public Base
{
public:
Derived() : derived_data(new int[6]) { std::cout << "D()\n"; }
~Derived() { std::cout << "~D()\n"; delete [] derived_data; }
protected:
int* derived_data;
};

int main()
{
Base* b = new Derived;
delete b;

return 0;
}


Output is:
B()
D()
~B()

which says that the derived class's destructor does not get called, thus
derived_data does not get destroyed properly.
 
E

E. Robert Tisdale

Marcus said:
E. Robert Tisdale said:

How about:

#include <iostream>

class Base {
public:
Base(void): base_data(new int[5]) { std::cout << "B(void)\n"; }
~Base(void) { std::cout << "~B(void)\n"; delete [] base_data; }
protected:
int* base_data;
};

class Derived: public Base {
public:
Derived(void): derived_data(new int[6]) { std::cout << "D(void)\n"; }
~Derived(void) { std::cout << "~D(void)\n"; delete [] derived_data; }
protected:
int* derived_data;
};

int main(int argc, char* argv[]) {
Base* b = new Derived;
delete b;
return 0;
}

Output is:
>
B(void)
D(void)
~B(void)

which says that the derived class's destructor does not get called,
thus derived_data does not get destroyed properly.

That's just a bad example.
The programmer (you) did something stupid and unnecessary.
How about:
> cat main.cc
#include <iostream>

class Base {
protected:
int* base_data;
public:
Base(void): base_data(new int[5]) {
std::cout << "B(void)" << std::endl; }
~Base(void) {
std::cout << "~B(void)" << std::endl;
delete [] base_data; }
};

class Derived: public Base {
protected:
int* derived_data;
public:
Derived(void) : derived_data(new int[6]) {
std::cout << "D(void)" << std::endl; }
~Derived(void) {
std::cout << "~D(void)" << std::endl;
delete [] derived_data; }
};

int main(int argc, char* argv[]) {
Derived* d = new Derived;
delete d;
return 0;
}
> g++ -Wall -ansi -pedantic -o main main.cc
> ./main
B(void)
D(void)
~D(void)
~B(void)

But, of course, allocation from free store was unnecessary.
You should have substituted:

int main(int argc, char* argv[]) {
Derived d;
return 0;
}

C++ is *not* Java.
You don't have a garbage collector to clean up after you.
This means that you should avoid allocating free storage explicity
and use automatic storage instead.
If you find yourself in a situation
where you *must* allocate free storage
(you have not shown us such a case yet),
then you must be more careful about how you do it.
An unnecessary virtual destructor might be *very* expensive
if your C++ compiler cannot optimize it away.
 
M

Marcus Kwok

E. Robert Tisdale said:
That's just a bad example.
The programmer (you) did something stupid and unnecessary.

What is stupid about using a base pointer to point to a derived type? I
hear it's pretty common when using polymorphism.
How about:
cat main.cc
#include <iostream>

class Base {
protected:
int* base_data;
public:
Base(void): base_data(new int[5]) {
std::cout << "B(void)" << std::endl; }
~Base(void) {
std::cout << "~B(void)" << std::endl;
delete [] base_data; }
};

class Derived: public Base {
protected:
int* derived_data;
public:
Derived(void) : derived_data(new int[6]) {
std::cout << "D(void)" << std::endl; }
~Derived(void) {
std::cout << "~D(void)" << std::endl;
delete [] derived_data; }
};

int main(int argc, char* argv[]) {
Derived* d = new Derived;
delete d;
return 0;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main
B(void)
D(void)
~D(void)
~B(void)
But, of course, allocation from free store was unnecessary.
You should have substituted:

int main(int argc, char* argv[]) {
Derived d;
return 0;
}

I just posted a minimal example demonstating the problem. What if the
class had a lot of data, and we needed a lot of objects of this type,
such that automatic variables are larger than the available stack space?
C++ is *not* Java.

I never said it was.
You don't have a garbage collector to clean up after you.
This means that you should avoid allocating free storage explicity
and use automatic storage instead.
If you find yourself in a situation
where you *must* allocate free storage
(you have not shown us such a case yet),
then you must be more careful about how you do it.

If you correctly implement RAII then it becomes less of an issue.
 
E

E. Robert Tisdale

Marcus said:
What is stupid about using a base pointer to point to a derived type?
I hear it's pretty common when using polymorphism.

But you weren't "using polymorphism".
The object's actual type was known in the scope where you deleted it.
You might as well write:

void* p = (void*)(new Derived);

if you are going to conceal the objects actual type.
(I'm not kidding.
Some C programmers actually write "object oriented" code this way.)
 
M

Marcus Kwok

E. Robert Tisdale said:
But you weren't "using polymorphism".

Yes, I know, but I was just demonstrating the problem with not having a
virtual destructor. I think you are missing the bigger picture.
There's even a FAQ about it.

FAQ entry:
http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7

When should my destructor be virtual?


When someone will delete a derived-class object via a base-class
pointer.

In particular, here's when you need to make your destructor virtual:

* if someone will derive from your class,

* and if someone will say new Derived, where Derived is derived from
your class,

* and if someone will say delete p, where the actual object's type
is Derived but the pointer p's type is your class.

Confused? Here's a simplified rule of thumb that usually protects you
and usually doesn't cost you anything: make your destructor virtual if
your class has any virtual functions. Rationale:

* that usually protects you because most base classes have at least
one virtual function.

* that usually doesn't cost you anything because there is no added
per-object space-cost for the second or subsequent virtual in your
class. In other words, you've already paid all the per-object
space-cost that you'll ever pay once you add the first virtual
function, so the virtual destructor doesn't add any additional
per-object space cost. (Everything in this bullet is
theoretically compiler-specific, but in practice it will be valid
on almost all compilers.)

Note: if your base class has a virtual destructor, then your destructor
is automatically virtual. You might need an explicit destructor for
other reasons, but there's no need to redeclare a destructor simply to
make sure it is virtual. No matter whether you declare it with the
virtual keyword, declare it without the virtual keyword, or don't
declare it at all, it's still virtual.

BTW, if you're interested, here are the mechanical details of why you
need a virtual destructor when someone says delete using a Base pointer
that's pointing at a Derived object. When you say delete p, and the
class of p has a virtual destructor, the destructor that gets invoked is
the one associated with the type of the object *p, not necessarily the
one associated with the type of the pointer. This is A Good Thing. In
fact, violating that rule makes your program undefined. The technical
term for that is, "Yuck."
 
K

Karl Heinz Buchegger

E. Robert Tisdale said:
But you weren't "using polymorphism".

Granted, but he easily could have:

int main()
{
std::vector< Base* > Objects;

for( int i = 0; i < 10; ++i ) {
std::cout << "What Object do you want ? (0 = Base, 1 = Derived) ";
int Type;
std::cin >> Type;
switch( Type ) {
case 0:
Objects.push_back( new Base );
break;
case 1:
Objects.push_back( new Derived );
break;
}
}

// do whatever you want to do with Objects.

for( int i = 0; i < 10; ++i )
Objects.foo();

// Get rid of them.

for( int i = 0; i < 10; ++i )
delete Objects;
}

No changes are required for Base and Derived (other then adding
a virtual foo function, but this was not the issue at hand which
turned around virtual dstructors and virtual constructors).
Yet the program works correctly (for a correct user input.) For this
it needs a virtual destructor and no virtual constructor.
 
R

Richard Herring

E. Robert Tisdale said:
Richard Herring wrote:

[I note that you don't care to address my substantive comments]
Unfortunately, you will continue to use them
whether they are appropriate or not
because you don't know when they are appropriate
and when they are not appropriate.

Your mind-reading skills need work, too.
 
R

Richard Herring

E. Robert Tisdale said:
But you weren't "using polymorphism".
The object's actual type was known in the scope where you deleted it.
You might as well write:

void* p = (void*)(new Derived);

if you are going to conceal the objects actual type.

My compiler objects to the next line,

p->DoStuff();

Does this "void" type of which you speak have any virtual functions I
could usefully call?
(I'm not kidding.
Some C programmers actually write "object oriented" code this way.)

This is comp.lang.C++
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top