带有模板的 C++ 运行时多态性

C++ runtime polymorphism with templates

我的程序有可观察对象和观察者。这是可观察的。

template <typename E>
class Observable {
  private:
    std::list<E> observers;

  public:
    void addObserver(E observer) {
        observers.push_back(observer);
    }

    void notifyAll() {
        for (auto & observer: observers) {
            observer.doSomething();
        }
    }
};

有一个基础 AnimalObserver 和派生观察者 CatObserverDogObserverAnimalObserver 有一个虚方法 void doSomething()CatObserverDogObserver 实现此方法。

class DogObserver : public AnimalObserver {

  public:
    DogObserver() = default;
    void doSomething() const {
        std::cout << "woof!";
    }
};
class CatObserver : public AnimalObserver {

  public:
    CatObserver() = default;
    void doSomething() const {
        std::cout << "meow!";
    }
};

我这样创建我的 Observable 和 运行 我的程序:

auto observable = Observable<AnimalObserver>();
auto cat = CatObserver();
auto dog = DogObserver();

observable.register(cat);
observable.register(dog);

observable.notifyAll();

发生的情况是 AnimalObserver 的 doSomething 方法被调用了两次,而不是 CatObserverDogObserver doSomething 方法被调用。我想调用派生 类 的 doSomething 方法,而不是父 类.

我想做的是保留一个从 AnimalObserver 派生的不同观察者的列表,并调用它们的 doSomething 行为而不是虚函数。

如果之前有人问过这个问题,我深表歉意,但我什至不确定要搜索什么。我正在阅读有关模板的内容,但迷路了。

唯一的问题是:您必须在 Observable class 中存储一个指向基 class AnimalObserver 的指针。您可以通过多种方式执行此操作。 unique_ptr<> 想到了。

这是一个带有一些提示的实现。神箭:https://godbolt.org/z/1Gq35G

#include <list>
#include <iostream>
#include <memory>
using namespace std;

struct AnimalObserver
{
    virtual ~AnimalObserver() { }
    // Hint 1: Declare abstract so that it won't even compile if you use the 
    //  base class directly in a collection - i.e. and not a pointer to the base.
    virtual void doSomething() const = 0;
};

template <typename E>
class Observable {
  private:
    typedef unique_ptr< E > _TyPtrContained;
    std::list<_TyPtrContained> observers;

  public:
    void addObserver( unique_ptr< E > && rrobserver ) {
        // Remember that inside a method that receives a rvalue-reference, 
        //  it is an lvalue-reference and must be std::move()'d when passing to a 
        //  method that accepts an rvalue-reference.
        observers.push_back( std::move( rrobserver ) );
    }

    void notifyAll() {
        for (auto & observer: observers) {
            observer->doSomething();
        }
    }
};

class DogObserver : public AnimalObserver {
  public:
    DogObserver() = default;
    // Hint 2: Use override to ensure that you are overriding an actual 
    //  virtual and not just declaring a method with a different signature 
    //  than the base.
    void doSomething() const override {
        std::cout << "woof!";
    }
};

class CatObserver : public AnimalObserver {
  public:
    CatObserver() = default;
    void doSomething() const override {
        std::cout << "meow!";
    }
};

int
main()
{
    auto observable = Observable<AnimalObserver>();
    // Hint 3: You can assign a unique_ptr<derived> to a unique_ptr<base>.
    unique_ptr< AnimalObserver > cat = make_unique< CatObserver >();
    unique_ptr< AnimalObserver > dog = make_unique< DogObserver >();

    // We move the local unique_ptrs into the addObserver method so that
    //  they are then moved into the list using the rvalue-reference version
    //  of list<>::push_back().
    observable.addObserver( std::move( cat ) );
    observable.addObserver( std::move( dog ) );

    observable.notifyAll();
    // And, voila, things work...
}