1

TL;DR

I am trying to pass a subclass into a function that expects the subclass's base class and then store a unique pointer of that base class in a vector in a third, completely separate class.

(C++ 11 and higher)

End TL;DR

I have 3 classes total and then my int main().

The base (abstract) class has a constructor and a virtual function. The base class constructor is implemented, the virtual function is not.

The second class is the subclass to the base class. It implements its own constructor and calls the base constructor. The second part of the sub class is the concrete implementation of the virtual base class function.

I then have a third class that has its own constructor. This third class has a function whose function header contains a reference to the base class. This same function then tries to pass this reference to the abstract class and then .push_back() the reference into a vector of std::unique_ptr of this abstract class. (Because I cannot directly have a vector of abstract class instances.)

My issue is that I am currently unable to get a version of this code to compile.

I have been referencing some resources online to try to solve my problem.

pass unique_ptr as an object
https://stackoverflow.com/questions/8114276/how-do-i-pass-a-unique-ptr-argument-to-a-constructor-or-a-function

adding elements of a vector of a base class
https://stackoverflow.com/questions/31410858/adding-elements-to-stdvector-of-an-abstract-class

can't access derived class method from pointer of base class - not entirely relavent, but good knowledge
https://stackoverflow.com/questions/23489554/cant-access-derived-class-method-from-pointer-of-type-base-class

I have created a shortened version of this problem in an example C++ executable that does not compile.

Here is the file:


/*
This script demonstrates my dilemma of trying to pass a subclass object
as a parameter to a function that expects the base class, and then 
take that passed in object in the function and add it to a vector of that object 
in a completely different class.
*/

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

class Baseclass
{
    public:
        Baseclass(int i)
        {
            lala = i;
        }

    // subclass MUST implement this method.
    virtual int addme(int d) = 0;

    protected:
        int lala;
};

class Subclass : Baseclass
{
    public:
        Subclass(int d, int l) : Baseclass(d)
        {
            blahblah = l;
        }

        int addme(int d)
        {
            return blahblah + lala + d;
        }

    protected:
        int blahblah;
};

class Mainclass
{
    public:
        Mainclass(uint blah)
        {
            anotherone = blah;
        }

        // This is the function I cannot seem to get to work.
        // How can I make the function parameter an abstract class?
        // The object being passed in is NOT abstract...
        bool addController(Baseclass & basecont)
        {
            // This line here does NOT compile!!
            // controllers.push_back(std::make_unique<What goes here?>(basecont));
            return true;
        }

    protected:
        uint anotherone;
        std::vector<std::unique_ptr<Baseclass>> controllers;
};

int main(int argc , char ** argv)
{
    // create subclassed controllers
    Subclass cont1 = Subclass(12, 23);
    Subclass cont2 = Subclass(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);
    // Add the subclased controllers to the main class
    // THESE 2 lines do not compile!!
    // mainbro.addController(cont1);
    // mainbro.addController(cont2);
    //
    return 0;
}

I figure that I am doing something very wrong, but I do not feel the process I outlined in itself is impossible. I just think that I am going about the problem wrong.

I have highlighted, in the script, where I am not sure what I should do and where the code breaks.

I may need to take an alternative approach to the problem, I just do not know what alternatives I have available.

Sisir
  • 4,584
  • 4
  • 26
  • 37
robotsfoundme
  • 418
  • 4
  • 18
  • Use `bool addController(std::unique_ptr basecount)` and pass `make_unique` objects to it. (BTW, excellently presented question.) – Raymond Chen Jan 03 '20 at 03:21
  • What are the error messages? Could you focus on one error instead of (at least) two? – JaMiT Jan 03 '20 at 03:50

3 Answers3

4

I see different way to fix your code, with different meaning.

  • Store pointers (main has ownership of the objects)

    class Mainclass
    {
    public:
    
        void addController(Baseclass& basecont)
        {
            controllers.push_back(&basecont);
        }
    
    protected:
        std::vector<Baseclass*> controllers;
    };
    
  • Transfer ownership

    class Mainclass
    {
    public:
    
        void addController(std::unique_ptr<Baseclass> basecont)
        {
            controllers.push_back(std::move(basecont));
        }
    
    protected:
        std::vector<std::unique_ptr<Baseclass>> controllers;
    };
    

    with main:

    int main()
    {
        auto cont1 = std::make_unique<Subclass>(12, 23);
        auto cont2 = std::make_unique<Subclass>(233, 2);
        Mainclass mainbro(23);
    
        mainbro.addController(std::move(cont1));
        mainbro.addController(std::move(cont2));
    }
    
  • Store copies

    class Mainclass
    {
    public:
    
        void addController(Baseclass& basecont)
        {
            controllers.push_back(basecont.clone());
        }
    
    protected:
        std::vector<std::unique_ptr<Baseclass>> controllers;
    };
    

    With

    class Baseclass
    {
    // ...
    public:
        virtual int addme(int d) = 0;
        virtual std::unique_ptr<Baseclass> clone() = 0;
    };
    
    class Subclass : Baseclass
    {
    // ...
    public:
        std::unique_ptr<Baseclass> clone() override { return std::make_unique<Subclass>(*this); }
    };
    
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

Whenever you use base pointers or references with virtual methods, always add a virtual destructor:

    virtual ~Baseclass() = default;

This prevents undefined behavior when the base pointers get deleted.

Next bit, use public inheritance to allow the compiler to implicitly upcast from unique_ptr<Subclass> to unique_ptr<Baseclass>:

class Subclass : public Baseclass

Your last issue is one of ownership. By having a vector of unique_ptr, you are saying that your class owns all those objects. But by declaring them on the stack in main you are saying that main owns them. Instead, use make_unique in the main routine, and transfer ownership with std::move:

        bool addController(std::unique_ptr<Baseclass> basecont)
        {
            controllers.push_back(std::move(basecont));
            return true;
        }

...

    auto cont1 = std::make_unique<Subclass>(12, 23);
    auto cont2 = std::make_unique<Subclass>(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);
    mainbro.addController(std::move(cont1));
    mainbro.addController(std::move(cont2));

All together:

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

class Baseclass
{
    public:
        Baseclass(int i)
        {
            lala = i;
        }

    virtual ~Baseclass() = default;

    // subclass MUST implement this method.
    virtual int addme(int d) = 0;

    protected:
        int lala;
};

class Subclass : public Baseclass
{
    public:
        Subclass(int d, int l) : Baseclass(d)
        {
            blahblah = l;
        }

        int addme(int d)
        {
            return blahblah + lala + d;
        }

    protected:
        int blahblah;
};

class Mainclass
{
    public:
        Mainclass(uint blah)
        {
            anotherone = blah;
        }

        bool addController(std::unique_ptr<Baseclass> basecont)
        {
            controllers.push_back(std::move(basecont));
            return true;
        }

    protected:
        uint anotherone;
        std::vector<std::unique_ptr<Baseclass>> controllers;
};

int main(int argc , char ** argv)
{
    // create subclassed controllers
    auto cont1 = std::make_unique<Subclass>(12, 23);
    auto cont2 = std::make_unique<Subclass>(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);
    mainbro.addController(std::move(cont1));
    mainbro.addController(std::move(cont2));
    return 0;
}

Demo: https://godbolt.org/z/EyQD6S

parktomatomi
  • 3,851
  • 1
  • 14
  • 18
  • Just a side thing: does resolving the inherited class object's type happen at compile time or run time? I am wondering if the methodology described above will have a serious performance hit (greater than 1~5 milliseconds). In other words, when I have a class method call a method on the object that is stored in the vector of `unique_ptr` types, will it take a long time to compute. – robotsfoundme Mar 09 '20 at 21:10
  • `virtual` members, by design, are dispatched at runtime (see: [vtable](https://en.wikipedia.org/wiki/Virtual_method_table)). The overhead is negligible, though, about the same as using a function pointer. On a beefy modern processor, the instruction cache will likely completely erase that miniscule overhead if you call the virtual method a lot. – parktomatomi Mar 10 '20 at 08:39
-2
#include <iostream>
#include <memory>
#include <vector>

class Baseclass
{
    public:
        Baseclass(int i)
        {
            lala = i;
        }

    // subclass MUST implement this method.
    virtual int addme(int d) = 0;

    protected:
        int lala;
};

class Subclass : public Baseclass
{
    public:
        Subclass(int d, int l) : Baseclass(d)
        {
            blahblah = l;
        }

        int addme(int d)
        {
            return blahblah + lala + d;
        }

    protected:
        int blahblah;
};

class Mainclass
{
    public:
        Mainclass(uint blah)
        {
            anotherone = blah;
        }

        // you need to make the function a template, otherwise 
        // you'll slice the top off the SubClass, and incorrectly
        // make a copy of the base class (which you can't do, 
        // because BaseClass is pure virtual)
        template<typename T>
        bool addController(T& basecont)
        {
            // dont push_back new unique_ptrs, emplace_back instead!
            controllers.emplace_back(new T(basecont));
            return true;
        }

    protected:
        uint anotherone;
        std::vector<std::unique_ptr<Baseclass>> controllers;
};

int main(int argc , char ** argv)
{
    // create subclassed controllers
    Subclass cont1 = Subclass(12, 23);
    Subclass cont2 = Subclass(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);

    // It's worth pointing out that these methods will take new copies of 
    // cont1 and cont2 (we don't want the mainbro instance to delete the 
    // memory for cont1 and cont2, since they are stack allocated)
    mainbro.addController(cont1);
    mainbro.addController(cont2);
    //
    return 0;
}
robthebloke
  • 9,331
  • 9
  • 12