Home > Net >  Polymorphism produces odd behaviour
Polymorphism produces odd behaviour

Time:05-16

I have a vector wrapper class which is aimed to simplify polymorphism:

class Shape
{
public:
    Shape(string Name) : name(Name) {}
    virtual ~Shape() = default;
    
    string name;
private:
};

class Point : public Shape
{
public:
    Point(string Name, float X, float Y) : Shape(Name), x(X), y(Y) {}

    float x = 0.0f;
    float y = 0.0f;
private:
};


// A vector wrapper for unique ptrs, simplifies polymorphism
template <typename T>
class Vector_UniquePtrs {
public:

    // Constructor with no parameters
    Vector_UniquePtrs() = default;
    
    // Constructor with initialiser list
    Vector_UniquePtrs(std::initializer_list<T> items)
    {
        m_Items.reserve(items.size());
        // fill `m_Items` from the initializer list by creating a unique_ptr from each element
        std::transform(items.begin(), items.end(), std::back_inserter(m_Items), [](const T& item) {
            return std::make_unique<T>(item);
        });
    };

    // Adds a Polymorphic Item (and returns a raw ptr to it)
    // usage: v.Add<sub_class>()
    template <class U, class... Args>
    T& Add(Args&&... args) {
        // Forward args to make_unique
        m_Items.emplace_back(std::make_unique<U>(std::forward<Args>(args)...));
        // Return a reference
        return *m_Items.back();
    }
    
    // Adds an Item (and returns a raw ptr to it)
    // usage: v.Add() (equivelent to v.Add<base_class>())
    template <class... Args>
    T& Add(Args&&... args) {
        // Forward to Add<U>
        return Add<T>(std::forward<Args>(args)...);
    }


    // Remove item from vector
    void Remove(size_t index) {
        Assert_IsValid(index);
        m_Items.erase(std::next(m_Items.begin(), index));
    }

    // Access item in vector
    T& operator[](size_t index)                 { Assert_IsValid(index); return *m_Items[index]; } // non-const
    const T& operator[](size_t index) const     { Assert_IsValid(index); return *m_Items[index]; } // const
    
    // Swaps items[n] and items[n_Next]
    void ItemSwap(size_t n, size_t n_Next) {
        Assert_IsValid(n); 
        Assert_IsValid(n_Next);
        std::swap(m_Items[n], m_Items[n_Next]);
    }
    
    // Gets the number of elements in the vector
    size_t Size() const                         { return m_Items.size(); }


protected:
    // The container
    std::vector<std::unique_ptr<T>> m_Items;
    
    // Validity function
    void Assert_IsValid(int index)              { assert(index > -1 && index < (int)Size()); }
};

But when called with the following:

Vector_UniquePtrs<Point> v;

v.Add<Point>("A", 1.0f, 10.0f);

Shape& ref = v[0];

v.Add<Point>("B", 2.0f, 20.0f);

ref = v[1];
       
Point& pt1 = dynamic_cast<Point&>(ref);
Point& pt2 = dynamic_cast<Point&>(v[1]);

cout << pt1.name << ": " << pt1.x << ", " << pt1.y << endl;
cout << pt2.name << ": " << pt2.x << ", " << pt2.y << endl;

The output is: B: 1, 10 B: 2, 20

The reference is somehow still 'thinking' it is looking at the v[0] for the inherited variables and v[1] for the base variables.. Can someone explain what is happening and how I should go about to resolve this?

Thanks!

CodePudding user response:

You can't reassign a reference. ref = v[1] copies the value of v[1] into the object referenced by ref. As the compiler only knows that ref is a Shape it calls Shape's assignment operator and therefore only copies the Shape members leaving the members from Point unchanged.

If you need to change what a reference points to then you probably need to use a pointer instead.

If you want assignment to work polymorphically then you'll need to add a virtual method which does that for you.

  • Related