Reputation: 532
I'm creating a 'basket' (container) to store 'items' (classes) that is derived from another class.
The idea is that the method select
returns the right class (derived), not the base.
#include <iostream>
#include <map>
#include <typeinfo>
class Item;
class Pencil;
class Basket
{
public:
Basket();
void insert(int id, Item item);
Item select(int id);
private:
std::map<int, Item> basket;
};
class Item
{
};
class Pencil : public Item
{
public:
Pencil() {}
void draw() { std::cout << "stackoverflow" << std::endl; }
};
Basket::Basket() {}
void Basket::insert(int id, Item item)
{
basket.insert(std::pair<int, Item>(id, item));
}
Item Basket::select(int id)
{
return basket[id];
}
int main(int argc, char ** argv)
{
Basket basket;
Pencil pencil;
basket.insert(1, pencil);
auto redpen = basket.select(1);
std::cout << typeid(redpen).name() << std::endl;
return 0;
}
Output: 4Item (expecting to be 6Pencil)
This way I can't use the method draw
because this isn't the item (class) that I added on the basket (container), it's converted to the base class.
Is there a way to return the right class, I mean, the derived one, but keeping that structure?
Thank you.
Upvotes: 0
Views: 105
Reputation: 145279
The example you requested in comments to the question:
#include <iostream>
#include <map>
#include <memory>
#include <typeinfo>
class Item;
class Pencil;
class Basket
{
public:
Basket();
void insert( int id, std::shared_ptr<Item> item );
auto select( int id ) -> std::shared_ptr<Item>;
private:
std::map< int, std::shared_ptr<Item> > basket;
};
class Item
{
public:
virtual ~Item() {}
};
class Pencil
: public Item
{
public:
Pencil() {}
void draw() { std::cout << "stackoverflow" << std::endl; }
};
Basket::Basket() {}
void Basket::insert( int const id, std::shared_ptr<Item> const item )
{ basket[id] = item; }
auto Basket::select( int const id )
-> std::shared_ptr<Item>
{ return basket[id]; }
int main()
{
Basket basket;
basket.insert(1, std::make_shared<Pencil>() );
auto redpen = basket.select( 1 );
std::cout << typeid(*redpen).name() << std::endl;
}
Upvotes: 1
Reputation: 1420
You would like to use C++ polymorphism with RTTI (Run-time Type Identification). One solution is to use pointer, thus to have:
class Basket
{
public:
Basket();
void insert(int id, Item *item);
Item *select(int id);
private:
std::map<int, Item*> basket;
};
This will need some dynamic allocations (new operations), and you can get rid of delete operation by using std::shared_ptr of memory header, with C++11.
Upvotes: 1
Reputation: 45654
You cannot return a derived class by value, because then you get slicing, which just means copying of the derived as if it was only a base, leaving everything added behind (including any hint it should have been a derived).
Instead, return a pointer to a newly-allocated derived as a base*. Don't forget cleaning that up though.
In order to catch failure to delete, you might look into smart-pointers, also for sharing cached return values.
When the returned value is actually present for as long as needed anyway, you could also return a const& base
.
Upvotes: 1