I Want a Vector of Derived Class Pointers as Base Class Pointers

I want a vector of derived class pointers as base class pointers

There is a copy constructor for a std::vector but it requires you to copy the exact same type of vector. Fortunately, there is another constructor which takes a pair of iterators and adds all the elements in the range, so you can do this:

vector<Animal*> animals(dogs.begin(),dogs.end());

This creates a new vector of Animal pointers by iterating through each Dog pointer. Each Dog pointer is converted to an Animal pointer as it goes.

Here is a more complete example (using C++11):

#include <vector>

struct Animal { };

struct Dog : Animal { };

struct Cat : Animal { };

struct Bird : Animal { };

int main(int,char**)
{
Dog dog1, dog2;
Cat cat1, cat2;
Bird bird1, bird2;
std::vector<Dog *> dogs = {&dog1,&dog2};
std::vector<Cat *> cats = {&cat1,&cat2};
std::vector<Bird *> birds = {&bird1,&bird2};
std::vector<std::vector<Animal *>> all_animals = {
{dogs.begin(),dogs.end()},
{cats.begin(),cats.end()},
{birds.begin(),birds.end()}
};
}

Vector of pointers to base class containing base and derived class objects - accessing derived-class specific variables

Yes it is possible. You only need to cast the pointer. The simplest syntax is:

((Derived*)e)->DerviedVariable

which is equivalent (modulo casting away constness, if any) to C++ish

static_cast<Derived*>(e)->DerviedVariable

The word “static” here reminds that there is no runtime checking: the compiler trusts you that e indeed points to an instance of Derived. If it doesn’t, undefined behavior occurs. The safer alternative is dynamic_cast:

Derived *ee = dynamic_cast<Derived*>(e);
if (ee)
x = ee->DerviedVariable;

It returns NULL if the object is not an instance of Derived. (Note that references can be casted as well, but as there is no NULL reference, dynamic_cast will throw instead if cast is not possible)

Nevertheless, using such casts is often considered a bad practice, for a reason. Virtual functions are preferable, mostly because using them don’t require you to even know the actual object type at the call point.

Making a vector of base class pointers and pass Derived class objects to it (Polymorphism)

I see that you have an array in Menu, let's say:

Shape* myshapes[10];

The shapes can be Rectangles, Triangles, Circles etc.
What you want is to be able to use the Menu's printDetails() method like this:

    void printDetails()
{
for(int i = 0; i < size; i++)
{
cout << "Index " << i << " has " << myshapes[i]->getShapeName() << endl;
}
}

The getShapeName() will return a string, e.g. "Rectangle" if it is Rectangle.
You will be able to do this with the help of pure virtual function. The pure virtual function must be in the abstract class Shape, which has:

virtual string getShapeName() = 0; //pure virtual

It means that we are expecting a definition for this function in the derived class. This way you will be able to use getShapeName() method using the Shape pointers in the shapes array, which will tell you whether the shape is Rectangle, Triangle, or Circle etc.

class Shape
{
public:
virtual string getShapeName() = 0;
};

class Circle : public Shape
{
private:
int radius;

public:
Circle(int r) { radius = r; cout << "Circle created!\n"; }
string getShapeName() { return "Circle"; }
};

class Arrow : public Shape
{
private:
int length;

public:
Arrow(int l) { length = l; cout << "Arrow created!\n"; }
string getShapeName() { return "Arrow"; }
};

class Polygon : public Shape
{
public:
virtual string getShapeName() = 0;
};

class Triangle : public Polygon
{
private:
int x, y, z;

public:
Triangle(int a, int b, int c) { x = a; y = b; z = c; cout << "Triangle created!\n"; }
string getShapeName() { return "Triangle"; }
};

class Rectangle : public Polygon
{
private:
int length;
int width;

public:
Rectangle(int l, int w){ length = l; width = w; cout << "Rectangle created!\n"; }
string getShapeName() { return "Rectangle"; }
};

To implement the addShape() method you can do this:

void addShape(Shape &shape)
{
myshapes[count] = &shape;
count++;
}

Also, keep in mind to pass the Shape by reference or by using pointer, in the addShape() method.
I hope this helps... Best of luck :-)

Best way to store std::vector of derived class in a host parent class

tldr: Use a variant or type erasure, depending on context.

What you are asking for in C++ would be described roughly as a value type or a type with value semantics. You want a type that is copyable, and copying just "does the right thing" (copies do not share ownership). But at the same time you want polymorphism. You want to hold a variety of types that satisfy the same interface. So... a polymorphic value type.

Value types are easier to work with, so they will make a more pleasant interface. But, they may actually perform worse, and they are more complex to implement. Therefore, as with everything, discretion and judgment come into play. But we can still talk about the "best practice" for implementing them.

Let's add an interface method so we can illustrate some of the relative merits below:

struct Base {
virtual ~Base() = default;
virtual auto name() const -> std::string = 0;
};

struct Derivative1: Base {
auto name() const -> std::string override {
return "Derivative1";
}
};

struct Derivative2: Base {
auto name() const -> std::string override {
return "Derivative2";
}
};

There are two common approaches: variants and type erasure. These are the best options we have in C++.

Variants

As you imply, variants are the best option when the set of types is finite and closed. Other developers are not expected to add to the set with their own types.

using BaseLike = std::variant<Derivative1, Derivative2>;

struct Host {
std::vector<BaseLike> derivativeList;
};

There's a downside to using the variant directly: BaseLike doesn't act like a Base. You can copy it, but it doesn't implement the interface. Any use of it requires visitation.

So you would wrap it with a small wrapper:

class BaseLike: public Base {
public:
BaseLike(Derivative1&& d1) : data(std::move(d1)) {}
BaseLike(Derivative2&& d2) : data(std::move(d2)) {}

auto name() const -> std::string override {
return std::visit([](auto&& d) { return d.name(); }, data);
}

private:
std::variant<Derivative1, Derivative2> data;
};

struct Host {
std::vector<BaseLike> derivativeList;
};

Now you have a list in which you can put both Derivative1 and Derivative2 and treat a reference to an element as you would any Base&.

What's interesting now is that Base is not providing much value. By virtue of the abstract method, you know that all derived classes correctly implement it. However, in this scenario, we know all the derived classes, and if they fail to implement the method, the visitation will fail to compile. So, Base is actually not providing any value.

struct Derivative1 {
auto name() const -> std::string {
return "Derivative1";
}
};

struct Derivative2 {
auto name() const -> std::string {
return "Derivative2";
}
};

If we need to talk about the interface we can do so by defining a concept:

template <typename T>
concept base_like = std::copyable<T> && requires(const T& t) {
{ t.name() } -> std::same_as<std::string>;
};

static_assert(base_like<Derivative1>);
static_assert(base_like<Derivative2>);
static_assert(base_like<BaseLike>);

In the end, this option looks like: https://godbolt.org/z/7YW9fPv6Y

Type Erasure

Suppose instead we have an open set of types.

The classical and simplest approach is to traffic in pointers or references to a common base class. If you also want ownership, put it in a unique_ptr. (shared_ptr is not a good fit.) Then, you have to implement copy operations, so put the unique_ptr inside a wrapper type and define copy operations. The classical approach is to define a method as part of the base class interface clone() which every derived class overrides to copy itself. The unique_ptr wrapper can call that method when it needs to copy.

That's a valid approach, although it has some tradeoffs. Requiring a base class is intrusive, and may be painful if you simultaneously want to satisfy multiple interfaces. std::vector<T> and std::set<T> do not share a common base class but both are iterable. Additionally, the clone() method is pure boilerplate.

Type erasure takes this one step more and removes the need for a common base class.

In this approach, you still define a base class, but for you, not your user:

struct Base {
virtual ~Base() = default;
virtual auto clone() const -> std::unique_ptr<Base> = 0;
virtual auto name() const -> std::string = 0;
};

And you define an implementation that acts as a type-specific delegator. Again, this is for you, not your user:

template <typename T>
struct Impl: Base {
T t;
Impl(T &&t) : t(std::move(t)) {}
auto clone() const -> std::unique_ptr<Base> override {
return std::make_unique<Impl>(*this);
}
auto name() const -> std::string override {
return t.name();
}
};

And then you can define the type-erased type that the user interacts with:

class BaseLike
{
public:
template <typename B>
BaseLike(B &&b)
requires((!std::is_same_v<std::decay_t<B>, BaseLike>) &&
base_like<std::decay_t<B>>)
: base(std::make_unique<detail::Impl<std::decay_t<B>>>(std::move(b))) {}

BaseLike(const BaseLike& other) : base(other.base->clone()) {}

BaseLike& operator=(const BaseLike& other) {
if (this != &other) {
base = other.base->clone();
}
return *this;
}

BaseLike(BaseLike&&) = default;

BaseLike& operator=(BaseLike&&) = default;

auto name() const -> std::string {
return base->name();
}

private:
std::unique_ptr<Base> base;
};

In the end, this option looks like: https://godbolt.org/z/P3zT9nb5o

Pass vector of derived class pointers to thread

To make your example work, there are a few mistakes that need to be corrected.

  1. As Consumer::doStuff is a non-static member function, you need an instance of Consumer to run it on.
  2. Because of above, Consumer needs a defined constructor
  3. The signature of doStuff needs to take a (reference to a) vector of Base pointers, not Base objects
  4. Thread t3 needs said instance of Consumer as its first forwarded parameter (to provide the this parameter.

Final working code:

#include<vector>
#include<thread>
#include<iostream>

using namespace std;

class Base
{
public:
Base() {};
void dosomething() {cout<<i<<endl;}
int i;
};

class Derived1 : public Base
{
public:
Derived1() {i = 5;}
};

class Derived2 : public Base
{
public:
Derived2() {i = 10;}
};

class Consumer
{
public:
Consumer() {}
void dostuff( vector<Base*> &pointervec) {cout<<pointervec.at(0)->i<<endl;}
};

int main( int argc, char ** argv )
{
Derived1 derived1;
Derived2 derived2;

Consumer c;
vector<Base*>pointervec;
pointervec.push_back(&derived1);
pointervec.push_back(&derived2);

std::thread t1(&Derived1::dosomething, &derived1);
std::thread t2(&Derived2::dosomething, &derived2);
std::thread t3(&Consumer::dostuff, &c, ref(pointervec));

t1.join();
t2.join();
t3.join();
}

Cast vector with base class pointers to back to subclass

I want to cast a vector of base class pointers to a vector of subclass pointers.

You can't.

Let me try to explain why that would be a problem if the compiler let you do that. Let's say you have another class derived from txt_base.

#include <vector>

class txt_base {
int a;
};

class txt_specific : public txt_base {
int b;

void do_stuff(){};
};

class txt_utf8 : public txt_base {
// ...
// Member data
// ...

void do_stuff(){};
};

int main() {
std::vector<txt_base *> x {new txt_specific(), new text_utf8()};

// Let's say te compiler allowed this
std::vector<txt_specific *> y = (std::vector<txt_specific *>) x;

txt_specific* ptr = y[1];
// Now you have ptr of type txt_specific that really points to a txt_utf8.
// Dereferencing ptr and using it member variables and member functions will
// lead to undefined behavior.
}

Casting a single object with txt_specific *o = (txt_specific *) x.front(); works but I cannot figure out how to cast the whole vector at once.

I hope I have explained why you should not attempt to do that. The choices for you are:

  1. Get a pointer to the base class and then perform a dynamic_cast. Please note that you'll need to change the base class to have at least one virtual member function before using dynamic_cast. Making the destructor virtual is the easiest choice. It is also appropriate when you want to delete derived class objects using a base class pointer.

    class txt_base {
    public:
    virtual ~txt_base() {}

    private:
    int a;
    };

    and

    txt_base* ptr = x.front();
    txt_specific* o = dynamic_cast<txt_specific *>(ptr);
    if ( o != nullptr )
    {
    // Use o
    }
  2. Keep a vector of derived class pointers. Then you don't have to worry about casting.



Related Topics



Leave a reply



Submit