Home > Enterprise >  How to use an abstract class rvalue reference member?
How to use an abstract class rvalue reference member?

Time:11-19

I have an abstract class Base and derived class Derived:

class Base
{
public:
    Base(int n) :_n(n) { _arr = new int[n]; }
    virtual ~Base() { delete[] _arr; }
    Base(Base&& other) { _n = other._n; _arr = other._arr; other._arr = nullptr; other._n = 0; }
    virtual void func() = 0;

private:
    int _n;
    int* _arr;
};

class Derived : public Base
{
public:
    Derived(int m, int n) : Base(n), _m(m) { _arr = new int[m]; }
    ~Derived() { delete[] _arr; }
    Derived(Derived&& other) : Base(std::move(other)) { _m = other._m; _arr = other._arr; other._arr = nullptr; other._m = 0; }
    void func() override { cout << "func"; }
private:
    int _m;
    int* _arr;
};

Then I have a class Bag which contains a rvalue reference of Base:

class Bag
{
public:
    Bag(Base&& b) : _b(std::move(b)) {}
    void func() { _b.func(); }
private:
    Base&& _b;
};

int main()
{
    Bag bag(Derived(1, 1));
    bag.func();
}

I use a rvalue reference member _b because I just want to take a temporary object in Bag's constructor. However, after bag.func() I got an error: abort() is called. It seems that bag._b's type changes from Derived to Base after the temporary object Derived(1,1) is destructed.

bag.func() works after I delete virtual ~Base() { delete[] _arr; }. Why? If I need Base's destructor, how can I change my code?

CodePudding user response:

Base&& _b is a reference to the temporary object, when that temporary is destroyed at the end of the line Bag bag(Derived(1, 1)); the reference becomes a dangling reference and any use of the reference is undefined behaviour.

You could change _b to a value instead but that would slice your object.

If you want to store a polymorphic object the only real option is to use a pointer (ideally a smart one). For example:

class Bag
{
public:
    Bag(std::unique_ptr<Base>&& b) : _b(std::move(b)) {}
    void func() { _b->func(); }
private:
    std::unique_ptr<Base> _b;
};

int main()
{
    Bag bag(std::make_unique<Derived>(1, 1));
    bag.func();
}

Note that you should also ensure that Base and Derived follow the rule of five otherwise you may encounter issues when you assign objects.

  • Related