Hi Bill,
> But then it is the pointer that is owned by the container,
> and destroying it doesn't call the destructor for the class to which
> it points. That means somehow hooking into the container's destructor
> (and the only way that I know of to do that is to derive a class from
> it) to loop accross the members and destroy them.
When I was trying to teach myself C++ earlier this summer, I ran across
the same problem that you describe.
First of all, storing pointers in the container classes is definitely the
way to go. Otherwise, when you store "solids" (as I call them), a *copy*
is made every time you do an insertion. You don't want this
overhead. Plus, as you rightly point out, "solids" are not polymorphic.
Anyway, I came up with two possible answers, although I don't know if
either is the "fully correct" way. Here goes:
1. Use the auto_ptr<> template class (#include <memory> to get this).
Basically everywhere you have a Foo* (Foo being the base class
of objects that you intend to store in the container), you
replace with auto_ptr<Foo> and then treat it just like a pointer.
auto_ptr's automagically delete themselves when they go out
of scope (i.e. when you delete the container, all auto_ptr<>
objects in the container will be deleted.) I have included
test case code at the end of this message that I wrote
earlier this summer to try to teach myself about auto_ptr's.
2. Wrap the container with another class (use composition instead
of inheritance). Create your own interface (like insert() and
remove(), etc.), and in the destructor explicitly delete each
object in the container.
I actually prefer #2. I find the auto_ptr<> syntax somewhat annoying
(dont get me started on "annoying C++ syntax issues" ;), and when you wrap
your container classes and provide your own interface, you can go back and
change your container some day in the future (e.g. from map to hash_map),
and none of the other code in the project will be affected by the change.
Hope this helps a little,
--Andy Foulks
If you are still interested in solution #1, here is my test code that
"explains" auto_ptr's: a description of the code, the output of a run, and
the code itself.
I compiled this using g++ (egcs-2.91.66) running under Red Had Linux
2.2.12-20, BTW.
In the following code, Foo is a class that stores a number, and Bar is
derived from it. This program instantiates a Foo and a Bar and stores
their pointers in an STL list container. The elements in the container
are iterated and printed. Finally, delete is called on the container
only. However, in the output of the program run, you can see that the
destructors of the Foo and the Bar are automagically called:
entered Foo @ 0x804c458 constructor
entered Foo @ 0x804c468 constructor
entered Bar @ 0x804c468 constructor
in Bar::print: num is 10
in Foo::print: num is 5
entered Bar @ 0x804c468 destructor
entered Foo @ 0x804c468 destructor
entered Foo @ 0x804c458 destructor
/****************************************************************************/
#include <iostream>
#include <list>
#include <memory>
class Foo {
protected: int num;
public:
Foo( int someNum ) : num(someNum) {
cerr << "entered Foo @ 0x" << hex << (int)this << " constructor"<<endl;
}
virtual ~Foo() {
cerr << "entered Foo @ 0x" << hex << (int)this << " destructor" <<endl;
}
virtual void print() {
cerr << "in Foo::print: num is " << dec << num << endl;
}
};
class Bar : public Foo {
public:
Bar( int someNum ) : Foo(someNum) {
cout << "entered Bar @ 0x" << hex << (int)this <<" constructor" <<endl;
}
virtual ~Bar() {
cerr << "entered Bar @ 0x" << hex << (int)this << " destructor" <<endl;
}
virtual void print() {
cerr << "in Bar::print: num is " << dec << num << endl;
}
};
int main()
{
typedef list< auto_ptr<Foo> > ListOfFoos;
ListOfFoos* container = new ListOfFoos();
auto_ptr<Foo> firstObj( new Foo(5) );
auto_ptr<Foo> secondObj( new Bar(10) );
container->push_back( secondObj );
container->push_back( firstObj );
ListOfFoos::iterator ptr;
for( ptr=container->begin(); ptr != container->end(); ptr++ )
(*ptr)->print();
delete container;
}
/****************************************************************************/
**********************************************************
To unsubscribe from this list, send mail to
[EMAIL PROTECTED] with the following text in the
*body* (*not* the subject line) of the letter:
unsubscribe gnhlug
**********************************************************