Search code examples
c++c++14shared-ptr

how to create a container of shared_ptr to abstract class


I'm trying to create a subject-observers system in C++, like the events system in C#. the observer class:

template <class T>
class Observer {
    int id;
public:
    static int nextId;
    Observer() : id(nextId++) {}
    virtual void handleEvent(const T&) = 0;
    virtual ~Observer();
};

the subject class:

template<class T>
class Subject {
    set<shared_ptr<Observer<T>>> observers;
public:
    Subject() : observers() {}
    void notify(const T&);
    void addObserver(Observer<T>& );
    void removeObserver(Observer<T>&);
    Subject<T>& operator+=(Observer<T>&);
    Subject<T>& operator-=(Observer<T>&);
    Subject<T>& operator()(const T&);
};

the problem is when I try to implement addObserver I don't know how to add the reference to the set.

I understand make_shared<Observer<T>> is creating a new instance, so when I tried make_shared<Observer<T>>(observer) I got an error of trying to create an abstract class :

error: invalid new-expression of abstract class type ‘Observer’

I tried shared_ptr<Observer<T>> observerPtr(observer) and it did not work as well :

error: no matching function for call to ‘std::shared_ptr >::shared_ptr(Observer&)’

how can I create a shared_ptr from a reference of an object derived from an abstract class?

what I'm trying to achieve is make this example work:

class TemperatureSensor : public Subject<int> {};

class AirConditioner : public Observer<int> {

    static int nextId;
    int id;

    void onTemperatureChange(int temperature){
        std::cout << "Air Conditioner #" << id << " got a report from TemperatureSensor, reading " << temperature << std::endl;
    }

public:
    AirConditioner() : Observer() {
        id = (++nextId);
    }

    void handleEvent(const int& param) override {
        onTemperatureChange(param);
    }
};
int AirConditioner::nextId = 0;

int main(){
    TemperatureSensor s;
    AirConditioner a,b,c;

    (((s += a) += b) += c);

    s(42);  // Should print:
            // Air Conditioner #1 got a report from TemperatureSensor, reading 42
            // Air Conditioner #2 got a report from TemperatureSensor, reading 42
            // Air Conditioner #3 got a report from TemperatureSensor, reading 42
}

Solution

  • To answer your question regarding implementing addObserver, the easiest way to get it work is to store the pointer in the container:

    template<class T>
    class Subject {
        set<Observer<T>*> observers;
    public:
        Subject() : observers() {}
        void addObserver(Observer<T>& o) { observers.insert(&o); }
    
    

    Depending on how you wish to manage the life cycle of the observer object, you could use set<shared_ptr<Observer<T>>>, but in this case, you should pass the share_ptr<Observer<T>> as parameter of addObserver(shared_ptr<Observer<T>>& o). If you have to use addObserver(Observer<T>&) interface and you want the "observers" set to co-manage the life cycle of the observer object, you could use std::enable_shared_from_this and make Observer a subclass of std::enable_shared_from_this.

    Hope this helps.