Barushkish
Barushkish

Reputation: 69

How to return an object of a class which is derived from an abstract class?

So, I have a generic operator. In this specific case its generic template T is supposed to be a pointer to an abstract base class. The point is, when I create T *t and try to return *t I get an error saying T can't be instantiated because it's an abstract class. Even though *t will always be an object of derived classes which aren't abstract. It doesn't accept it, because T is after all abstract type. Basically, what I want to achieve is to get a reference to the *t returned from the operator to main() and then assign to it objects of derived classes in main().

I understand that there are questions about slicing and polymorphism. But I am trying to understand how can I achieve it in different ways. It's a completely different question which deals with the following specific code.

Code is something like this:

Template <class T>
class myArr{
T & operator [] (int index){
T*t;
Arr[0] = *t;
Return arr[0[;

 }}

Main(){
Base * b=new derived;
myArr<Base>[17]=*b;

// Point is, I can make it of type pointers and maybe it can 
//work but I prefer assigning *b to myArr because I want 
to //assign objects themselves

}

My question is, how can I return the object in itself from the operator? I want to return something to which I can assign a derived object . I am trying to do it with pointer of type abstract base, so as to achieve polymorphism so it can contain any object of derived classes.

Upvotes: 0

Views: 842

Answers (2)

Ted Lyngmo
Ted Lyngmo

Reputation: 117318

I tried making something out of the code you showed by using std::unique_ptr<Base> in your array. It shows how you can replace objects in the array (base_arr[0] = ...) and how to update existing objects in the array (*base_arr[0] = ...). I added two derived classes carrying different types (a std::string and an int) and a lot of debug prints so it's easier to follow what's happening when you run it.

#include <iostream>
#include <vector>
#include <memory>
#include <string>

// Your container wrapper with some functions
template<class T>
class myArr {
public:
    using type = T;
    template<class... Args>
    decltype(auto) emplace_back(Args&&... args) {
        return arr.emplace_back(std::forward<Args>(args)...);
    }

    T& operator[](std::size_t index) { return arr[index]; }
    auto begin() const { return arr.begin(); }
    auto end() const { return arr.end(); }
    T extract_front() {
        T retval(std::move(arr.front()));
        arr.erase(arr.begin());
        return retval;
    }

private:
    std::vector<T> arr{};
};
//----------------------------------------------------------------------------------
struct Base {
    Base() = default;
    Base(const Base&) = default;
    Base(Base&&) = default;
    virtual ~Base() = 0;
    virtual Base& operator=(const Base&) = 0;
    virtual Base& operator=(Base&&) = 0;
    virtual void print() = 0;
};

Base::~Base() {}

Base& Base::operator=(const Base&) {
    std::cout << "Base& Base::operator=(const Base&)\n"; // nothing real to copy here
    return *this;
}

Base& Base::operator=(Base&&) {
    std::cout << "Base& Base::operator=(Base&&)\n"; // nothing real to move here
    return *this;
}
//----------------------------------------------------------------------------------
struct der_1 : public Base {
    der_1(const std::string& value) : Base(), m_value(value) {
        std::cout << "der_1(" << m_value << ") converting\n";
    }
    der_1(const der_1& rhs) : Base(rhs), m_value(rhs.m_value) {
        std::cout << "der_1(" << m_value << ") copy\n";
    }
    der_1(der_1&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
        std::cout << "der_1(" << m_value << ") move\n";
    }
    ~der_1() { std::cout << "~der_1(" << m_value << ")\n"; }

    der_1& operator=(const der_1& rhs) {
        std::cout << "der_1& der_1::operator=(const der_1&)\n";
        if(this == &rhs) return *this; // no self-assignment
        Base::operator=(rhs);          // copy the Base part of rhs
        m_value = rhs.m_value;         // copy the der_1 specific part
        return *this;
    }

    der_1& operator=(der_1&& rhs) {
        std::cout << "der_1& der_1::operator=(der_1&&)\n";
        Base::operator=(std::move(rhs));  // move the Base part of rhs
        m_value = std::move(rhs.m_value); // move the der_1 specific part
        return *this;
    }

    // override Base's copy assignment
    Base& operator=(const Base& rhs) override {
        std::cout << "Base& der_1::operator=(const Base&)\n";
        // downcasting may throw bad_cast
        const der_1& rhsref = dynamic_cast<const der_1&>(rhs);
        return *this = rhsref; // call standard copy assignment
    }

    // override Base's move assignment
    Base& operator=(Base&& rhs) override {
        std::cout << "Base& der_1::operator=(Base&&)\n";
        // downcasting may throw bad_cast
        der_1& rhsref = dynamic_cast<der_1&>(rhs);
        return *this = std::move(rhsref); // call standard move assignment
    }

    void print() override { std::cout << "der_1::print(" << m_value << ")\n"; }

private:
    std::string m_value;
};
//----------------------------------------------------------------------------------
struct der_2 : public Base {
    der_2(int value) : Base(), m_value(value) {
        std::cout << "der_2(" << m_value << ") converting\n";
    }
    der_2(const der_2& rhs) : Base(rhs), m_value(rhs.m_value) {
        std::cout << "der_2(" << m_value << ") copy\n";
    }
    der_2(der_2&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
        std::cout << "der_2(" << m_value << ") move\n";
    }
    ~der_2() { std::cout << "~der_2(" << m_value << ")\n"; }

    der_2& operator=(const der_2& rhs) {
        std::cout << "der_2& der_2::operator=(const der_2&)\n";
        if(this == &rhs) return *this; // no self-assignment
        Base::operator=(rhs);          // copy the Base part of rhs
        m_value = rhs.m_value;         // copy the der_2 specific part
        return *this;
    }

    der_2& operator=(der_2&& rhs) {
        std::cout << "der_2& der_2::operator=(der_2&&)\n";
        Base::operator=(std::move(rhs));  // move the Base part of rhs
        m_value = std::move(rhs.m_value); // move the der_2 specific part
        return *this;
    }

    // override Base's copy assignment
    Base& operator=(const Base& rhs) override {
        std::cout << "Base& der_2::operator=(const Base&)\n";
        // downcasting may throw bad_cast
        const der_2& rhsref = dynamic_cast<const der_2&>(rhs);
        return *this = rhsref; // call standard copy assignment
    }

    // override Base's move assignment
    Base& operator=(Base&& rhs) override {
        std::cout << "Base& der_2::operator=(Base&&)\n";
        // downcasting may throw bad_cast
        der_2& rhsref = dynamic_cast<der_2&>(rhs);
        return *this = std::move(rhsref); // call standard move assignment
    }

    void print() override { std::cout << "der_2::print(" << m_value << ")\n"; }

private:
    int m_value;
};
//----------------------------------------------------------------------------------
int main() {
    myArr<std::unique_ptr<Base>> base_arr;
    {
        {
            std::cout << "-- put pointers to objects of derived classes in base_arr --\n";
            base_arr.emplace_back(std::make_unique<der_1>("howdy"));
            base_arr.emplace_back(std::make_unique<der_2>(10));

            std::cout << "\n-- print what we've got --\n";
            for(auto& b : base_arr) b->print();

            std::cout << "\n-- set new value for an existing object, by copying --\n";
            der_1 something_to_copy("something_to_copy");
            *base_arr[0] = something_to_copy;

            std::cout << "\n-- set new value for an existing object, by moving --\n";
            *base_arr[0] = der_1("something_to_move");

            std::cout << "\n-- try to assign a der_2 to a der_1 --\n";
            try {
                *base_arr[0] = der_2(666);
            } catch(const std::exception& ex) {
                std::cout << "Exception: " << ex.what() << "\n";
            }

            std::cout << "\n-- replace a der_1 object with a der_2 object --\n";
            base_arr[0] = std::make_unique<der_2>(20);

            std::cout << "\n-- destroying something_to_copy since it goes out of "
                         "scope --\n";
        }
        std::cout << "\n-- stuff in base_arr --\n";
        for(auto& b : base_arr) b->print();

        std::cout << "\n-- extract front, got:\n";
        auto ptr = base_arr.extract_front();
        ptr->print();
        std::cout << "\n-- the above dies, goes out of scope --\n";
    }

    std::cout << "\n-- base_arr is about to be destroyed --\n";
}

Upvotes: 0

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

You cannot return an object of an abstract class. You need to return a reference or a pointer, preferably a smart one.

You cannot store an object of an abstract class. You need to store a reference or a pointer, preferably a smart one.

You cannot pass an object of an abstract class to a function as a parameter. You need to pass a reference or a pointer, preferably a smart one.

See the pattern?

Objects of abstract classes do not exist on their own. They only exist as subobjects of derived class objects, and can only be accessed via references or pointers obtained from pointers to said derived class objects.

There is no way around it. You cannot do it differently. Use smart pointers. They are the right tool for tthe job.

I want to return something to which I can assign a derived object .

This is kind of possible, depending on what exactly you want, but way more complicated than necessary. Assignment and polymorphic hierarchies don't mix well. I would not recommend that, especially if you are not sure what exactly you want. Assign, store, and pass around smart pointers.

Upvotes: 1

Related Questions