2

Tour and GuidedTour. GuideTour extends Tour. I create a list of these items and add them to a vector.

 list = new vector<Tour>(); 
 list->push_back(Tour("FP001", "Fun Park 3 Day Pass", 110.00));
 list->push_back(Tour("BG002", "Botanical Gardens Entry Pass", 30.00));
 list->push_back(GuidedTour("SK003", "Learn to Ski Adventure Tour", 240.00, "28/07/2008", "Zail S", 25));
 list->push_back(Tour("OZ004", "Open Range Zoo Entry Pass", 45.00));
 list->push_back(GuidedTour("AB005", "Abseiling for Beginners Tour", 120.00, "15/07/2008", "Rex P", 35));
 list->push_back(GuidedTour("RA006", "White Water Rafting Tour", 200.00, "22/06/2008", "Clint R", 16));

Then I want to go thorugh this array and check the type of these Objects

void TourManager::callDisplayOnEach() {
    for (vector<Tour>::iterator it = list->begin(); it != list->end(); ++it) {
        if (typeid(*it) == typeid(GuidedTour)) {
            cout << "Guided Tour" << "\n";
        }
        else { 
            cout << "NOT Guided Tour : " << typeid(*it).name() << "\n"; 
        }
        //(*it).display();
    }
}

However it always returns NOT a Guided Tour option.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
Achintha Gunasekara
  • 1,165
  • 1
  • 15
  • 29

4 Answers4

2

Refering to Mat's link:

By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behaviour as well

So create a vector of

list = new vector<Tour*>(); 

Rather than

list = new vector<Tour>(); 
Saksham
  • 9,037
  • 7
  • 45
  • 73
2

As the comment above says, it is because of slicing. Since you store values in your vector, and not references or pointers, therefore when you copy the values into the container (yes, they are copied!), then every object is truncated to the base class Tour.

0

I agree with other answers below that this is a slicing problem, althrough I would make it a vector of unique_ptrs so that it will manage the memory upon release for you. See below my version. Also, added a virtual destructor in the base class which is a good idea anytime you are referring to a derived class through a base pointer to avoid slicing if destructing via a base pointer.

#include <iostream>
#include <memory>
#include <vector>

class Tour
{

  public:
  Tour(const std::string& tt, const std::string& desc, double p  ) : tourType_(tt), description_(desc), price_(p){}

  virtual void display () const
  {
    std::cout << "Standard Tour" << std::endl;
  }

  virtual ~Tour() {};

  protected:
  std::string tourType_;
  std::string description_;
  double price_;
};

class GuidedTour : public Tour
{

  public:
  GuidedTour(const std::string& tt, const std::string& desc, double p, const std::string& date, const std::string& name, int age  ) : Tour(tt,desc,p), date_(date), guideName_(name), guideAge_(age) {}


  void display () const 
  {
    std::cout << "Guided Tour" << std::endl;
  }

  private:
  std::string date_;
  std::string guideName_;
  int guideAge_;
};


int main ( int argc, char *argv[] )
{
  std::vector<std::unique_ptr<Tour> > v;

  v.emplace_back(new Tour("FP001", "Fun Park 3 Day Pass", 110.00));
  v.emplace_back(new Tour("BG002", "Botanical Gardens Entry Pass", 30.00));
  v.emplace_back(new GuidedTour("SK003", "Learn to Ski Adventure Tour", 240.00, "28/07/2008", "Zail S", 25));
  v.emplace_back(new Tour("OZ004", "Open Range Zoo Entry Pass", 45.00));
  v.emplace_back(new GuidedTour("AB005", "Abseiling for Beginners Tour", 120.00, "15/07/2008", "Rex P", 35));
  v.emplace_back(new GuidedTour("RA006", "White Water Rafting Tour", 200.00, "22/06/2008", "Clint R", 16));

  for ( auto& i : v )
  {
    i->display();
  }
}
bjackfly
  • 3,236
  • 2
  • 25
  • 38
0

Like it is said, there is Slicing.

As you vector stores Tour object (Base class), all the derived object specific members of the object (GuidedTour) to be stored will get sliced off during the copy to act as Base class object.

The solution is to stores pointer of the base class :

std::vector<Tour*> list;

Since you are storing a pointer, there is no object copy anymore and no object slicing.

The best to do this would be to use smart pointers to do not have to do manually the memory management :

std::vector<std::unique_ptr<Tour>> list;

But if you use smart pointers, you will have to use std::vector::emplace_back() instead of push_back :

std::vector<std::unique_ptr<Tour>> list;

list.emplace_back(new Tour);
list.emplace_back(new GuidedTour);
list.emplace_back(new Tour);
list.emplace_back(new GuidedTour);
list.emplace_back(new Tour);

Live example.

But those techniques imply to use C++11.

Community
  • 1
  • 1
Pierre Fourgeaud
  • 14,290
  • 1
  • 38
  • 62