By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,419 Members | 1,124 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,419 IT Pros & Developers. It's quick & easy.

inheritance and STL

P: n/a
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

--
Harvey J Cohen, Ph. D

"The difference between fiction and reality is that fiction has to make
sense." - Tom Clancy
Jul 23 '05 #1
Share this Question
Share on Google+
6 Replies


P: n/a
Harvey J Cohen, Ph. D. wrote:
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

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

Jul 23 '05 #2

P: n/a
> 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
Jul 23 '05 #3

P: n/a
"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.
Jul 23 '05 #4

P: n/a
"Harvey J Cohen, Ph. D." <Ha***************************@adelpREALLYNOSPAMhi a.net> wrote in message news:<Xn*********************************@216.196. 97.142>...
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
Jul 23 '05 #5

P: n/a
On Wed, 30 Mar 2005 23:59:42 -0600, "Harvey J Cohen, Ph. D."
<Ha***************************@adelpREALLYNOSPAMhi a.net> wrote:
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
Jul 23 '05 #6

P: n/a
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

Jul 23 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.