Making a Vector of Instances of Different Subclasses

Create a vector of sub classes?

You need extra indirection because State is a polymorphic base class. You can do this using std::unique_ptr from <memory>.

#include <memory>

std::vector<std::unique_ptr<State>> states;
states.emplace_back(new Menu());

It is very important to use std::unique_ptr<State> and not State*, because of exception safety. Consider the following:

std::vector<State*> states;
states.push_back(new Menu());
foo(); // what if foo throws an exception?
// the next line wouldn’t get executed!
for (auto ptr : states) delete ptr;

In contrast, std::unique_ptr uses RAII to make sure the objects are always deleted if the vector goes out of scope, even in the case of early returns or exceptions. For further reference, see The Definitive C++ Book Guide and List.

Member values not accessed in vector of instances of different subclasses

The problem is due to a misunderstanding on inheritance. If you redefine your Mesh as follows, it would work as expected:

class Mesh : public Entity {

public:
//int index; // No don't redefine: here you'd have two different indexes
Mesh(int x) {this->index=x;};
void hit() override {} // extraa safety: use override instead of virtual to be sure to override
};

Online demo

Not related to the problem, but some more thoughts:

  • You can of course only use the interface of the base class (here Entity).
  • It is not very prudent to expose the index member publicly. A safer option would be to have it private, and access it via a public getter. If the index should be set only at construction, the Entity constructor should take care of it, otherwise you could consider a public setter.

c++ Call subclass method in vector of base class type

How can I get access to the function com() which is only in object of subclass FiatMoney?

You would have to type-cast a Currency* pointer into a FiatMoney* pointer, but you can only do that when the pointer is actually pointing at a valid FiatMoney object.

When you know the Currency* pointer is pointing at a FiatMoney object, you can use static_cast at compile-time, eg:

static_cast<FiatMoney*>(Wallet[0])->com();

Otherwise, use dynamic_cast at runtime to test the type of object being pointed at:

FiatMoney *fm = dynamic_cast<FiatMoney*>(Wallet[0]);
if (fm) fm->com();

Can I have method in class FiatMoney without previously declarate it as virtual function of Currency class?

Technically yes, though some people would argue that is not a good design choice when dealing with polymorphic classes.

Is this proper way of storing objects of different subclasses?

Technically yes, although Currency should have a virtual destructor. You have to delete the objects you new, and a virtual destructor will allow you to call delete on a Currency* pointer to invoke derived class destructors without type-casting the pointer. And as such, you should consider storing smart pointers, like std::unique_ptr, instead of raw pointers, so that delete is called automatically for you.

Vector of subclasses

This is a serious bug:

virtual wsk clone()const{
r1 tmp=r1(*this);
return &tmp; // returning pointer to a local object
}

You are returning a pointer to a local object on stack. After clone() returns, tmp will be destroyed.

Dont use raw owning pointers. Use smart pointers instead, ex. unique_ptr .

It's also a good habit to use override in subclasses, so the compiler can emit an error if you override the wrong method. You don't need virtual in subclasses, if subclasses are not meant to be derived from (it's an old way of marking virtual methods in a pre C++-11 era).

So the method becomes:

unique_ptr<robot> clone() const override
{
return make_unique<robot>(new r1(*this));
}

And for the list of robots you could use a vector of smart pointers vector<unique_ptr<robot>>, so that all objects vector are automaticaly destroyed and memory freed.

C++11 vector holding 2 different subclasses, but not at the same time

boost::variant< std::vector<A>, std::vector<boost::variant<B,C>> > describes your structure.

In C++17, replaced boost:: with std::. The two are very similar, but not identical.

If you cannot use boost, you'll have to basically write a variant of your own.

Use is a bit awkward, but so are your type restrictions. Use gets easier in C++14 with auto-lambdas (which I believe boost variant visitor supports).

In C++14/17 you might do something like (pseudocode)

template<class F, class T, class=std::enable_if_t<!is_variant<T>{}>>
decltype(auto) visit( F&& f, T&& t ) {
return std::forward<F>(f)(std::forward<T>(t));
}
my_special_vec v;

visit( [&](auto&& v){
for (auto&& e:v) {
Base* b = visit( [&](auto&& e){
e.do_operation();
return std::addressof(e);
}, decltype(e)(e));
b->do_virtual_operation();
}
}, v );

which should non-virtually call e.do_operation() on whichever of A, B or C your vector contains, then call do_virtual_operation() on the instance as a Base.

And this is the easier, C++14 version. The C++11 version gets awkward with function objects and other mess.



Related Topics



Leave a reply



Submit