SH.0x90
SH.0x90

Reputation: 532

Returning the derived instead of the base class

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;
}

Live preview

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

Answers (3)

Cheers and hth. - Alf
Cheers and hth. - Alf

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

AntiClimacus
AntiClimacus

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

Deduplicator
Deduplicator

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

Related Questions