Solved

vector of objects with inheritance in C++

Posted on 2004-09-19
7
593 Views
Last Modified: 2013-12-14
Hi

What's the correct way to set up a data structure for the following, using sound OO and design-pattern methods?

1. I have an class hierarchy for zoo animals, including FourLeggedAnimals, TwoLeggedAnimals, AnimalWithBeaks, etc etc.
2. I want to print out a table of all my zoo animals
3. For any animal that has a beak, I want the table to show what colour their beak is

I want the animals to be stored in a vector<ZooAnimal *>, and I want an iterator to be used to move through the list of animals. How can I access methods that are particular to AnimalWithBeak given that my pointer is to an object of type 'ZooAnimal'? What is the correct approach?

Thanks in advance!
0
Comment
Question by:jdpipe
7 Comments
 

Expert Comment

by:G00fy
ID: 12099739
class ZooAnimal
{
public:
  ZooAnimal ( ) : m_Name("ZooAnimal") { }
  virtual void Print( ) { cout << m_Name << endl; }

protected:
  string m_Name;
};

class FLAnimal : public ZooAnimal
{
public:
  FLAnimal( ) : m_Name("FLAnimal") { }
// No need to override Print
};

class TLAnimal : public ZooAnimal
{
public:
  TLAnimal( ) : m_Name("TLAnimal") { }
// No need to override Print
};

class BeakAnimal : public ZooAnimal
{
public:
  BeakAnimal( const string& colour ) : m_Name("BeakAnimal"), m_colour( colour ) { }
  virtual void Print( ) { cout << m_Name << " (Colour: " << m_colour << ")" << endl; }

protected:
  string m_colour;
};


That's how I should do it ;-)

Now you can iterator like:

for ( vector<ZooAnimal*>::const_iterator ci = zoo.begin(); ci != zoo.end(); ci++ )
  (*ci)->Print();
0
 
LVL 30

Expert Comment

by:Axter
ID: 12100497
If you use a constant iterator, you will not be able to access a none-constant member function.
You need either to make the function constant, or to use none-constant iterator.
0
 
LVL 30

Expert Comment

by:Axter
ID: 12100510
Example:

for ( vector<ZooAnimal*>::iterator i = zoo.begin(); i != zoo.end();++i)
 (*i)->Print();

Also, it's better to have prefix increments over postfix increments, as in above example:
If you really want more efficiency, you can also initialize the begin() value to a for-loop variable, and then make comparison to the variable.

for (vector<ZooAnimal*>::iterator i = zoo.begin(), e =  zoo.end(); i != e;++i)
 (*i)->Print();
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 30

Accepted Solution

by:
Axter earned 125 total points
ID: 12100545
>>How can I access methods that are particular to AnimalWithBeak given that my pointer
>>is to an object of type 'ZooAnimal'?

For a really good OO approach, you wouldn't want to do this from your interface class.

Your interface class is ZooAnimal, and you should only access members that are common to the interface.

Otherwise, it would require your code to know more about what type of ZooAnimal class you have (which it shouldn't).
Then you would have to do some type of casting.

for (vector<ZooAnimal*>::iterator i = zoo.begin(), e =  zoo.end(); i != e;++i)
{
  if (????) //If TLAnimal type
  {
       TLAnimal *My_TLAnimal = (*i);
       My_TLAnimal->SpecialFunctions();
  }
}

Above code *not* good OO design.
0
 
LVL 7

Author Comment

by:jdpipe
ID: 12101319
Axter, I think you got closest to answering my query. Basically I needed to know if there were a way to set it up so that I could access my special 'beakColour' function for the two-legged animal, given that I'm storing a mixed collection of ZooAnimals in my vector.

You're saying that there isn't, unless I do that casting, but that sort of casting is bad form from OO design's point of view.

Cheers!
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12101488
>>>> I have an class hierarchy for zoo animals

That sounds as if you already have finished the class design. If so, you should post the class hierarchy here as it isn't easy to have a stringent object hierarchy for zoo animals when using attributes like the number of legs or having a beak rather than categories like mammals, birds and fishes. For example i would derive BeakAnimal from TwoLeggedAnimal as i don't know any animal with four legs and a beak. However, there could be an insect or a fish having something that could be called a 'beak'. Then, G00fy's hierarchy is correct and you would need multi-inheritance for a class Birds (all of them have a beak i suppose).

class ZooAnimal
{
public:
  ZooAnimal (const string& name = "ZooAnimal" ) : m_Name(name) { }
  virtual void Print( ) { cout << m_Name << endl; }

protected:
  string m_Name;
};

class FourLeggedAnimal : virtual public ZooAnimal
{
public:
    FourLeggedAnimal( ) : ZooAnimal("FourLeggedAnimal") { }
    // virtual void Print( ) { cout << m_Name << " has four legs " << endl; }
};

class TwoLeggedAnimal : virtual public ZooAnimal
{
public:
   TwoLeggedAnimal( ) : ZooAnimal("TwoLeggedAnimal") { }
    // virtual void Print( ) { cout << m_Name << " has two legs " << endl;
};

class BeakAnimal : virtual public ZooAnimal
{
public:
  BeakAnimal( const string& colour ) : ZooAnimal("BeakAnimal"), m_colour( colour ) { }
  virtual void Print( ) { cout << m_Name << " (Beak Colour: " << m_colour << ")" << endl; }

protected:
  string m_colour;
};

class Bird : public TwoLeggedAnimal, public BeakAnimal
{
public:
  Bird( const string& beakColour ) : ZooAnimal("Bird"), BeakAnimal( beakColour ) { }
  // virtual void Print( ) { cout << m_Name << " (Colour: " << m_colour << ")" << endl; }

protected:
};

However, i got a compile warning stating 'Bird' : inherits 'BeakAnimal::Print' via dominance, what means that when calling Bird::Print() BeakAnimal::Print() is called as it has an implementation of Print(). But, if i added a Print() implementationsof TwoLeggedAnimal i got a compiler error: "ambiguous inheritance of 'TwoLeggedAnimal::Print' "

So, you have to override Bird::Print(), e. g. by

    void Bird::Print( ) { cout << m_Name << " (Beak Colour: " << m_colour << ")" << endl; }

But the number of legs got lost now or you have to copy all baseclass code to all derived classes (you see that rendundancy already with m_name output) what is bad style.

There are two ways out:

1. Change the Print functions that way:


  void ZooAnimal::Print(bool printed ) { if (!printed) cout << m_Name;  }

  void FourLeggedAnimal::Print( bool printed )
       { ZooAnimal::Print(printed ); cout << " (4 legs)"; }

  void TwoLeggedAnimal::Print( bool printed )
      { ZooAnimal::Print(printed ); cout << " (2 legs)"; }

  void BeakAnimal::Print(bool printed  )  
       { ZooAnimal::Print(printed);    cout << " (Beak Colour " << m_colour << ")"; }

  void Bird::Print( bool printed) { TwoLeggedAnimal::Print(false); BeakAnimal::Print(true);  }


2. Use pure virtual functions and print from baseclass:

class ZooAnimal
{
public:
  ZooAnimal (const string& name = "ZooAnimal" ) : m_Name(name) { }
  virtual bool   hasLegs()      { return false; }
  virtual int    numberOfLegs() { return -1; }
  virtual bool   hasBeak()      { return false; }
  virtual string getBeakColor() { return "unknown"; };
  virtual void Print( )
  {  cout << m_Name;
     if (hasLegs()) cout << " has " << numberOfLegs()  << " legs"; }
     if (hasBeak()) cout << " ( Beak is " << getBeakColour() << ")"; }
  }
protected:
  string m_Name;
};

class FourLeggedAnimal : virtual public ZooAnimal
{
public:
    FourLeggedAnimal( ) : ZooAnimal("FourLeggedAnimal") { }
    virtual bool hasLegs()      { return true; }
    virtual int  numberOfLegs() { return 4; }
};

class TwoLeggedAnimal : virtual public ZooAnimal
{
public:
    TwoLeggedAnimal( ) : ZooAnimal("TwoLeggedAnimal") { }
    virtual bool hasLegs()      { return true; }
    virtual int  numberOfLegs() { return 2; }
};

class BeakAnimal : virtual public ZooAnimal
{
public:
  BeakAnimal( const string& colour ) : ZooAnimal("BeakAnimal"), m_colour( colour ) { }
  virtual bool   hasBeak()      { return true; }
  virtual string getBeakColor() { return m_colour; };

protected:
  string m_colour;
};

class Bird : public TwoLeggedAnimal, public BeakAnimal
{
public:
  Bird( const string& beakColour ) : ZooAnimal("Bird"), BeakAnimal( beakColour ) { }

protected:
};

However, with that you can't easily enhance your class hierarchy as the baseclass has to know all attributes by providing appropriate virtual functions.

Regards, Alex
0
 
LVL 7

Author Comment

by:jdpipe
ID: 12140142
I found these helpful:

http://www.cplusplus.com/doc/tutorial/tut5-4.html
http://www.cplusplus.com/doc/tutorial/tut5-3.html

I think dynamic_cast combined with <exception>'s std::bad_cast exception are the safe way to do this, even if it's not the best OO in the world...

I guess the design pattern in question is the Strategy or State pattern but it doesn't seem to fit perfectly.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to use NetBeans IDE 8.0 for Windows to connect to a MySQL database. Open Services Panel: Create a new connection using New Connection Wizard: Create a test database called eetutorial: Create a new test tabel called ee…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now