简体   繁体   中英

how to create a list from a set of derived classes?

Let's say I have a set of following classes:

class animal
{
// members, constructors and other methods...

public:
    virtual void what_do_you_eat() { cout << "i eat generic food" << endl; }
}

class cat : public animal
{
// members, constructors and other methods...

public:
    void what_do_you_eat() { cout << "i eat cat food" << endl; }
}

class dog : public animal
{
// members, constructors and other methods...

public:
    void what_do_you_eat() { cout << "i eat dog food" << endl; }
}

and I want to create a list (any container would be fine, but let's choose std::list<> for this example):

std::list<animal> animals
animals.push_back( dog() );
animals.push_back( cat() );

but when I try to iterate over the list I get this output:

for(auto itr : animals)
{
    itr.what_do_you_eat();
}
// output:
// i eat generic food
// i eat generic food
// i eat generic food
// i eat generic food
// ...

I tried then to make a list of pointers ( std::list<animal *> animals ), output was correct, but there are many issues with this solution:

  • because the standard says that stl containers can't hold a const type I can't use a list of std::list<animal * const> animals and pointers held by animals can be overwritten anywhere in my program.
  • I need to release them manually.

Question:
Is there any possibility to pass a derived class into an stl container, through a reference to a base class, and still get the right output?

Is there any workaround to do this correctly without pointers? Currently, I am writing a project that depends heavily on class inheritance, and I am stuck right now because I need to create a list of objects of different types (all of these types inherit directly or indirectly from a single base class) and it seems to be impossible.

solutions involving boost library are acceptable.

When you are handling polymorphism container must use pointers of any kind. It is best to use smart pointer and in your case std::unique_ptr will do the job perfectly.

So for example:

std::list<std::unieque_ptr<animal>> animals;

animals.emplace_back(new dog {});
animals.emplace_back(new cat {});

Note base class animal has to have a virtual destructor! It is best to define this as an abstract class:

class animal
{
// NO members, NO constructors only pure virtual methods
public:
    virtual ~animal() {};
    virtual void what_do_you_eat() = 0; 
};

In your code, list<animal> should be list<animal*> and then you must change other lines of the code accordingly.

See the code below. It works as you expected.

class animal
{
// members, constructors and other methods...

public:
             animal(){}
    virtual ~animal(){}

    virtual void what_do_you_eat() { cout << "i eat generic food" << endl; }
};

class cat : public animal
{
// members, constructors and other methods...


public:
             cat(){}
    virtual ~cat(){}

    void what_do_you_eat() { cout << "i eat cat food" << endl; }
};

class dog : public animal
{
// members, constructors and other methods...

public:
             dog(){}
    virtual ~dog(){}

    void what_do_you_eat() { cout << "i eat dog food" << endl; }
};

int main() {

    std::list<animal*> animals;
    animal *a=new animal();
    dog *d=new dog();
    cat *c = new cat();

    animals.push_back( a );
    animals.push_back( d );
    animals.push_back( c );

    for (std::list<animal*>::iterator itr=animals.begin(); itr!=animals.end(); ++itr)
    {

        (*itr)->what_do_you_eat();
    }

  return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM