как создать контейнер shared_ptr для абстрактного класса - PullRequest
1 голос
/ 17 января 2020

Я пытаюсь создать систему наблюдателей в C ++, как систему событий в C#. класс наблюдателя:

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

класс субъекта:

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&);
};

проблема в том, когда я пытаюсь реализовать addObserver Я не знаю, как добавить ссылку на набор .

Я понимаю, make_shared<Observer<T>> создает новый экземпляр, поэтому при попытке make_shared<Observer<T>>(observer) я получил ошибку при попытке создать абстрактный класс:

ошибка: недопустимый новый -выражение абстрактного типа класса 'Observer'

Я пытался shared_ptr<Observer<T>> observerPtr(observer), и это не сработало также:

ошибка: нет подходящей функции для вызова 'std :: shared_ptr> :: shared_ptr (Observer &) '

как я могу создать shared_ptr из ссылки на объект, полученный из абстрактного класса?

чем я являюсь попытаться добиться - заставить этот пример работать:

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
}

Ответы [ 4 ]

1 голос
/ 17 января 2020

Чтобы ответить на ваш вопрос относительно реализации addObserver, самый простой способ заставить его работать, это сохранить указатель в контейнере:

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

В зависимости от того, как вы будете sh управлять жизненным циклом объект наблюдателя, вы можете использовать set<shared_ptr<Observer<T>>>, но в этом случае вы должны передать share_ptr<Observer<T>> в качестве параметра addObserver(shared_ptr<Observer<T>>& o). Если вам нужно использовать интерфейс addObserver(Observer<T>&) и вы хотите, чтобы набор «наблюдатели» совместно управлял жизненным циклом объекта-наблюдателя, вы можете использовать std :: enable_shared_from_this и сделать Observer подклассом std :: enable_shared_from_this.

Надеюсь, это поможет.

0 голосов
/ 17 января 2020

C# позволяет легко игнорировать время жизни Observer в Subject, и все будет (в основном) нормально. C ++ не позволяет вам игнорировать время жизни.

Если Subject должен поддерживать все Observer в живых, то вам нужно пройти std::shared_ptr<Observer>, а не Observer &.

Если вы уверены, что Observer s всегда переживет Subject или всегда может позвонить removeObserver до того, как они d ie, тогда вы все равно можете передать Observer & и сохранить std::reference_wrapper<Observer>.

Если все ваши Observer s принадлежит std::shared_ptr s, но вы не хотите, чтобы Subject влиял на их время жизни, передачу и хранение std::weak_ptr<Observer>.

0 голосов
/ 17 января 2020

как я могу создать shared_ptr из ссылки на объект, производный от абстрактного класса?

Вы не можете создать shared_ptr в абстрактный класс.

Но вы можете сделать что-то для работы с кодом.

Возможное решение - определить базовый класс Наблюдатель не абстракт , удаление =0. Затем, чтобы убедиться, что вы действительно чистая виртуальная функция не вызывается из базового класса Observer, вы можете вызвать исключение при реализации метода базового класса Observer. Таким образом, при вызове класса Observer он вызывает исключение, в другом случае (например, в классе AirConditioner ) он выполняет свою работу без вызова исключения.

Примечание: Это просто обходной путь для этого случая, попробуйте переосмыслить дизайн кода для лучшей реализации

0 голосов
/ 17 января 2020

как я могу создать shared_ptr из ссылки на объект, производный от абстрактного класса?

В общем, вы не можете. Общий указатель на абстрактную базу может указывать только на конкретные экземпляры. Обычно вы не можете узнать конкретный тип через базовую ссылку во время компиляции.

Возможно, вам следует изменить интерфейс так, чтобы функция принимала общий указатель в качестве аргумента, а не ссылку.

...