20

In C++ how do I get to use a shared pointer of a pure abstract class without using reset and new?

The example is a little contrived but illustrates the problem I am encountering.

Look at the run() method: reset works but the commented out lines don't...

#include <iostream>
#include <map>
#include <memory>

using namespace std;

class Interf {
public:
    virtual void doSomething()=0;
};


class Foo : public Interf {
public:
    Foo() { cout << "Foo constructed\n"; }

    shared_ptr<Interf> innerInterf;

    void doSomething() {
        cout << "Foo:doSomething()\n";
        innerInterf->doSomething();
    }

    void run() {
        cout << "run() called\n";

        innerInterf.reset(new Bar());                     // this works

        //Bar b;
        //innerInterf = make_shared<Interf>((Interf)b);   // how can i get this to work?
    }

    class Bar : public Interf {
    public:
        Bar() { cout << "Bar constructed\n"; }
        ~Bar(){ cout << "Bar destroyed\n"; }

        void doSomething() { cout << "Bar:doSomething()\n"; }
    };
};

int main() {
    Foo foo;
    foo.run();

    Interf *interf;
    interf = &foo;
    cout << "interf.doSomething()\n";
    interf->doSomething();
}
bolov
  • 72,283
  • 15
  • 145
  • 224
LostLad
  • 203
  • 2
  • 6
  • 1
    `Interf` is an **abstract class** because it has a **pure virtual function**. There’s no such thing as a pure abstract class. – Pete Becker Jan 19 '21 at 00:54
  • 2
    You should probably add a virtual (possibly empty) destructor to `Interf`; otherwise, `Bar`s destructor won't be called when `innerInterf` is overwritten, which is a problem if the destructor is non-trivial. – Erlkoenig Jan 19 '21 at 10:00
  • @Erlkoenig -- good point. But the problem isn't that the proper destructor won't be called; it's that the behavior is undefined if you delete an object of a derived type through a pointer to the base. Yes, often the result is that only the base destructor is called, but that's not something you'll always see and, more importantly, it's not something you can rely on. – Pete Becker Jan 19 '21 at 14:48

2 Answers2

22

Instead of new Bar write make_shared<Bar>, as you are making Bar, not Interf.

Bar b;
innerInterf = make_shared<Bar>(b); // copy constructed? (no idea if that is what you want?)
innerInterf = make_shared<Bar>();  // calls Bar::Bar()

Because I see non-virtual destructors, you might want to research about when to use virtual destructors, and also about rule of 0/3/5, if you haven't already.

Anyway, nice question and good MCVE.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
3

One of the limitations of an abstract class is that it cannot create its objects directly, instead pointing to subclass objects as base-class Pointers.To facilitate the use of polymorphism, we often need to define virtual functions in the base class.

In many cases, it is unreasonable for the base class itself to generate objects.For example, animal as a base class can be derived from tiger, finch and other subclasses, but the animal itself to generate objects is obviously unreasonable.

In order to solve the above problems, the concept of pure virtual Function is introduced, and the Function is defined as a pure virtual Function(method: virtual returnType Function()= 0;)..To make a derived class non-abstract, the compiler requires that pure virtual functions in the derived class be overloaded to achieve polymorphism.Classes that also contain pure virtual functions are called abstract classes and cannot generate objects.This is a good solution to the above two problems.

For example, in the drawing program, Shape as a base class can derive circles, rectangles, squares, trapezoids, etc. If I want the sum of areas, then I can use an array of Shape *, just call the derived class's area() function in turn.You can't define it as an array without an interface, because it could be a circle, it could be a square, and it could be a rectangle, and so on.

Lichard
  • 41
  • 5