在 C++ 中创建观察者设计模式的好方法

Good way to create observer design pattern in C++

我正尝试在 C++ 中实现观察者设计模式,如下所示

#include <iostream>
#include <vector>

using namespace std;

class observer
{
    public:
        observer() = default;
        ~observer() = default;

        virtual void notify() = 0;
};

class subject
{
    vector <observer *> vec;

    public:
        subject() = default;
        ~subject() = default;

        void _register(observer *obj)
        {
            vec.push_back(obj);
        }

        void unregister(observer *obj)
        {
            int i;
            for(i = 0; i < vec.size(); i++)
            {
                if(vec[i] == obj)
                {
                    cout << "found elem. unregistering" << endl;
                    vec.erase(vec.begin() + i);
                    break;
                }
            }

            if(i == vec.size())
            {
                cout << "elem not found to unregister" << endl;
            }
        }

        void notify()
        {
            vector <observer *>::iterator it = vec.begin();
            while(it != vec.end())
            {
                (*it)->notify();
                it ++;
            }
        }
};

class obsone : public observer
{
    void notify()
    {
        cout << "in obsone notify" << endl;
    }
};

class obstwo : public observer
{
    void notify()
    {
        cout << "in obstwo notify" << endl;
    }
};

int main()
{
    subject sub;

    obsone *one = new obsone();
    obstwo *two = new obstwo();

    sub._register(one);   
    sub._register(two);
    sub.notify();

    sub.unregister(one);
    sub.notify();

    //delete two;
    //sub.notify();

    return 0;
}

我正在明确地向主题注册对象。这是正确的做法还是我只需要通过观察者 class 注册。上面的做法有什么问题吗?

下面是使用回调集合中的 lambda 和 function 对象执行回调的示例。

细节可能有很大差异!所以,这段代码不是“那种”方式,而只是你的代码以一种特定的方式重写,出于无数的可能性。但它有望展示现代 C++ 中的一般思想。

#include <iostream>
#include <functional>               // std::function
#include <stdint.h>                 // uint64_t
#include <unordered_map>            // std::unordered_map
#include <utility>                  // std::move    
#include <vector>                   // std::vector
using namespace std;

namespace my
{
    using Callback = function<void()>;
    template< class Key, class Value > using Map_ = unordered_map<Key, Value>;

    class Subject
    {
    public:
        enum Id: uint64_t {};

    private:
        Map_<uint64_t, Callback> m_callbacks;

        static auto id_value()
            -> uint64_t&
        {
            static uint64_t the_id;
            return the_id;
        }

    public:
        auto add_listener( Callback cb )
            -> Id
        {
            const auto id = Id( ++id_value() );
            m_callbacks.emplace( id, move( cb ) );
            return id;
        }

        auto remove_listener( const Id id )
            -> bool
        {
            const auto it = m_callbacks.find( id );
            if( it == m_callbacks.end() )
            {
                return false;
            }
            m_callbacks.erase( it );
            return true;
        }

        void notify_all() const
        {
            for( const auto& pair : m_callbacks )
            {
                pair.second();
            }
        }
    };
}

struct Observer_1
{
    void notify() { cout << "Observer_1::notify() called." << endl; }
};

struct Observer_2
{
    void notify() { cout << "Observer_2::notify() called." << endl; }
};

auto main()
    -> int
{
    my::Subject     subject;
    Observer_1      one;
    Observer_2      two;

    using Id = my::Subject::Id;
    const Id listener_id_1 = subject.add_listener( [&]{ one.notify(); } );
    const Id listener_id_2 = subject.add_listener( [&]{ two.notify(); } );

    cout << "After adding two listeners:" << endl;
    subject.notify_all();
    cout << endl;

    subject.remove_listener( listener_id_1 )
        and (cout << "Removed listener 1." << endl)
        or (cout << "Did not find registration of listener 1." << endl);
    cout << endl;
    cout << "After removing or attempting to remove listener 1:" << endl;
    subject.notify_all();
}