inheritance and STL

  • Thread starter Harvey J Cohen, Ph. D.
  • Start date
H

Harvey J Cohen, Ph. D.

I've been using STL for some time, but I have discovered an issue that
I'm sure has been resolved, but I'm not sure on the proper way to use STL
to solve this issue.

The issue is inheritance and the use of STL containers.

Consider a base class Fruit, with derived classes Orange, Banana and
Apple.

I want to create an STL container of "Fruits", which may be Orange,
Banana or Apple. I don't have any generic Fruit.

I create the container (lets say a deque) as follows:

deque<Fruit> theCrate;

In my application, I create anOrange, aBanana and anApple as follows:

Banana aBanana;
Orange anOrange;
Apple anApple;

Lets say that class Fruit has a virtual method "Eat". I declare a
virtual Eat for each derived class of Fruit (that is, for Banana, Apple
and Orange).

I add instances of Orange, Apple and Banana to my deque as follows:

theCrate.push_back (anOrange);
theCrate.push_back (aBanana);
theCrate.push_back (anApple);

if I perform the following code

for (deque<Fruit>::iterator pF = theCrate.begin ()
pF != theCrate.end ();
pF++)
pF->Eat ();

What I get is three executions of the method Fruit::Eat.
The fact that anOrange, aBanana and anApple are derived from Fruit, but
not a fruit, is lost on STL.

What is happening is that STL is enforcing using the Fruit contructor,
not the Orange, Banana or Apple constructors, and the nature of each
fruit is lost.

If I use the following container

deque<Fruit*> theCrate,

and store points to aBanana, anOrange and anApple, when I execute

(*pF)->Eat ();

I get executions of Banana::Eat, Orange::Eat and Apple::Eat.

Is there any way around this problem? I don't want to store pointers.

Regards,

Harvey
 
M

Mark P

Harvey said:
I've been using STL for some time, but I have discovered an issue that
I'm sure has been resolved, but I'm not sure on the proper way to use STL
to solve this issue.

The issue is inheritance and the use of STL containers.

Consider a base class Fruit, with derived classes Orange, Banana and
Apple.

I want to create an STL container of "Fruits", which may be Orange,
Banana or Apple. I don't have any generic Fruit.

I create the container (lets say a deque) as follows:

deque<Fruit> theCrate;

In my application, I create anOrange, aBanana and anApple as follows:

Banana aBanana;
Orange anOrange;
Apple anApple;

Lets say that class Fruit has a virtual method "Eat". I declare a
virtual Eat for each derived class of Fruit (that is, for Banana, Apple
and Orange).

I add instances of Orange, Apple and Banana to my deque as follows:

theCrate.push_back (anOrange);
theCrate.push_back (aBanana);
theCrate.push_back (anApple);

if I perform the following code

for (deque<Fruit>::iterator pF = theCrate.begin ()
pF != theCrate.end ();
pF++)
pF->Eat ();

What I get is three executions of the method Fruit::Eat.
The fact that anOrange, aBanana and anApple are derived from Fruit, but
not a fruit, is lost on STL.

What is happening is that STL is enforcing using the Fruit contructor,
not the Orange, Banana or Apple constructors, and the nature of each
fruit is lost.

As it must. An STL container "owns" its contained objects, which means
if you have a deque of Fruit objects, the container must allocate space
for each such object it holds. Given this, it should be clear that a
derived class object which may, in general, be larger than the base
class object cannot fit in the space allocated by the container.

The general consequence of this is "slicing" of the derived object when
the base object is constructed within the container, so despite your
best efforts, the container really is holding only a Fruit, not an Orange.

-Mark
 
J

James Aguilar

Is there any way around this problem? I don't want to store pointers.

From what I've read, the answer to this question is, "No."

- JFA1
 
S

Siemel Naran

"Harvey J Cohen, Ph. D."

deque<Fruit> theCrate;
deque<Fruit*> theCrate,
and store points to aBanana, anOrange and anApple, when I execute

(*pF)->Eat ();

I get executions of Banana::Eat, Orange::Eat and Apple::Eat.

Is there any way around this problem? I don't want to store pointers.

There is no way as the others have pointed out. Are you are worried about
memory issues, such as when you copy theCrate1 into theCrate2 you need to
make a copy of the underlying Fruit or increment a reference count otherwise
you have two pointers pointing to the same object and when you delete both
your program crahses, or when you delete theCrate then delete the pointed-to
objects?

If so, then consider using a reference counted pointer class, for example:

deque<boost::shared_ptr<Fruit> > theCrate;

You can also write your own smart pointer class whose copy constructor calls
the clone member function on its underlying pointer.

// requied traits: T::clone()
template <class T>
class deep_ptr {
public:
deep_ptr(T * ptr = NULL) : d_ptr(ptr) { }
deep_ptr(const deep_ptr& that) : d_ptr(that.d_ptr->clone()) { }
deep_ptr& operator=(const deep_ptr&);
~deep_ptr() { delete d_ptr; }
T* operator->() const { return d_ptr; }
T& operator*() const { return *d_ptr; }
private:
T * d_ptr;
};

It seems like a useful class, and I wonder if boost has something like it.
Reference counting mechanisms like shared_ptr may cause some performance
degradation in multi-threaded environments, where we have to use (platform
dependent) mutex locks to prevent two threads from changing the same memory
at once.
 
A

Andrea

Harvey J Cohen said:
I want to create an STL container of "Fruits", which may be Orange,
Banana or Apple. I don't have any generic Fruit.

I create the container (lets say a deque) as follows:

deque<Fruit> theCrate;

In my application, I create anOrange, aBanana and anApple as follows:

Banana aBanana;
Orange anOrange;
Apple anApple;

Lets say that class Fruit has a virtual method "Eat". I declare a
virtual Eat for each derived class of Fruit (that is, for Banana, Apple
and Orange).

I add instances of Orange, Apple and Banana to my deque as follows:

theCrate.push_back (anOrange);
theCrate.push_back (aBanana);
theCrate.push_back (anApple);


What you can do is use the pimple idiom to create a fruit handle that
has value semantics:

class fruit_handle
{
std::auto_ptr<fruit> pimpl;
public:
fruit_handle(const fruit_handle& X) : pimpl(X.pimple->clone()) {}
template<typename T> fruit_handle(const T& X) : pimple(X.clone()) {}
};

Clearly, your base class must have a virtual clone function and, of
corse, a virtual destructor.

struct fruit
{
virtual ~fruit = 0;
virtual void clone()const =0;
virtual void Eat() =0;
//any other virtual method you like
};


Now you define the container

std::deque<fruit_handle> theCrate;

and you can do

theCrate.push_back (anOrange);
theCrate.push_back (aBanana);
theCrate.push_back (anApple);

since fruit_handle now has value semantics, i.e., you can copy handles
around.

Andrea
 
R

Roland Pibinger

I've been using STL for some time, but I have discovered an issue that
I'm sure has been resolved, but I'm not sure on the proper way to use STL
to solve this issue.
The issue is inheritance and the use of STL containers.
Consider a base class Fruit, with derived classes Orange, Banana and
Apple.

I want to create an STL container of "Fruits", which may be Orange,
Banana or Apple. I don't have any generic Fruit.
I create the container (lets say a deque) as follows:

deque<Fruit> theCrate;

In my application, I create anOrange, aBanana and anApple as follows:
Banana aBanana;
Orange anOrange;
Apple anApple;

Lets say that class Fruit has a virtual method "Eat". I declare a
virtual Eat for each derived class of Fruit (that is, for Banana, Apple
and Orange).

I add instances of Orange, Apple and Banana to my deque as follows:

theCrate.push_back (anOrange);
theCrate.push_back (aBanana);
theCrate.push_back (anApple);

What you are doing here is called 'object slicing'
( http://c2.com/cgi/wiki?ObjectSlicing ).
if I perform the following code

for (deque<Fruit>::iterator pF = theCrate.begin ()
pF != theCrate.end ();
pF++)
pF->Eat ();

What I get is three executions of the method Fruit::Eat.
The fact that anOrange, aBanana and anApple are derived from Fruit, but
not a fruit, is lost on STL.

What is happening is that STL is enforcing using the Fruit contructor,
not the Orange, Banana or Apple constructors, and the nature of each
fruit is lost.

If I use the following container

deque<Fruit*> theCrate,

and store points to aBanana, anOrange and anApple, when I execute

(*pF)->Eat ();

I get executions of Banana::Eat, Orange::Eat and Apple::Eat.

Is there any way around this problem? I don't want to store pointers.

Yes and No. No, because you can't avoid pointers in polymorphic
situations. Yes, because you can avoid the hassle with pointers by
using a special container for pointers, e.g.
http://www.codeproject.com/vcpp/stl/ptr_vecto.asp .

Best regards,
Roland Pibinger
 
P

Puppet_Sock

Harvey J Cohen, Ph. D. wrote:
[stl and derived classes, snip]
Consider a base class Fruit, with derived classes Orange, Banana and
Apple.

And don't forget: A container of child is not a child of container
of parent. You can't park your nuclear submarine in the motorcycle
parking lot. As somebody else suggested, you need to store some
kind of handle object that will deal with the specific kind of
fruit it is going to hold onto.
Socks
 

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
474,001
Messages
2,570,254
Members
46,849
Latest member
Fira

Latest Threads

Top