简体   繁体   中英

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
}

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.

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

In general, you cannot. A shared pointer to abstract base can only be pointed to concrete instances. You generally cannot know the concrete type through a base reference at compile time.

You should probably change the interface so that the function accepts a shared pointer as argument, instead of a reference.

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

You cannot create a shared_ptr to an abstract class.

But you can do something to do the code work.

A possible solution is to define the base class Observer not abstrastct , removing the =0 . Then for ensure you actual pure virtual function to not be called on base class Observer in the a bad way you can throw exception in the implementation of the method of the base class Observer. So that when called on Observer class it raises an exception, in the other case ( for example in the AirConditioner class) it does its work without raising exception.

Note: This is a just a workaround for this case, try to re-think your code design for a better implementation

C# makes it easy to ignore the lifetime of Observer in Subject , and things will (mostly) be ok. C++ doesn't let you ignore lifetimes.

If a Subject should keep all the Observer s alive, then you need to pass std::shared_ptr<Observer> , not Observer & .

If you are sure your Observer s always outlive your Subject , or can always call removeObserver before they die, then you can still pass Observer & , and store std::reference_wrapper<Observer> .

If all your Observer s are owned by std::shared_ptr s, but you don't want Subject to affect their lifetime, pass and store std::weak_ptr<Observer> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM