如何实现观察者模式而不必绑定到特定的类?

时间:2017-01-19 21:47:30

标签: c++ c++11 observer-pattern

我想观察在功能级别删除的对象。换句话说,我会说在一个函数内部,观察该对象并允许我询问一些事情,以确定它是否已被删除。完成此功能后,请不要再观察它了。

这是我目前的API:

template <typename T>
class DeleteReporter
{
    std::pair<T*, bool>* obj_deleted_pair;
public:
    DeleteReporter(T* pObject);
    operator bool();
    ~DeleteReporter();
};

template <typename T>
DeleteReporter<T> make_DeleteReporter(T* pObject);

template <typename T>
void MarkDeleted(T* pObject);

以下是实施:

template <typename T>
std::vector<std::pair<T*, bool>>& obj_deleted_pairs()
{
    static std::vector<std::pair<T*, bool>> obj_deleted_pairs;
    return obj_deleted_pairs;
}

template <typename T>
DeleteReporter<T> make_DeleteReporter(T* pObject)
{
    return DeleteReporter<T>(pObject);
}

template <typename T>
void MarkDeleted(T* pObject)
{
    auto it = std::find_if(obj_deleted_pairs<T>().begin(), obj_deleted_pairs<T>().end()
        , [pObject](std::pair<T*, bool>& obj_deleted_pair)
        {
            return obj_deleted_pair.first == pObject;
        });
    if (it != obj_deleted_pairs<T>().end())
    {
        it->second = true;
    }
}

template <typename T>
DeleteReporter::DeleteReporter(T* pObject)
{
    obj_deleted_pairs<T>().emplace_back(pObject, false);
    obj_deleted_pair = &*obj_deleted_pairs<T>().rbegin();
}

template <typename T>
DeleteReporter::operator bool()
{
    return obj_deleted_pair->second;
}

template <typename T>
DeleteReporter::~DeleteReporter()
{
    obj_deleted_pairs<T>().erase(obj_deleted_pairs<T>().begin()
        + std::distance(&*obj_deleted_pairs<T>().begin(), obj_deleted_pair));
}

要使用,析构函数中会调用MarkDeleted()并传递this。然后在函数中,它将使用DeleteReporter实例化make_DeleteReporter()传递对象进行观察。稍后,将查询DeleteReporter对象以确保在实例化之后某个时间没有删除该对象,然后再尝试与之对话。

最初,我没有将其作为模板,而是功能已经void*。然后我意识到,如果对象是多次继承的,则指针可能无法正确匹配。

如我所做的那样使用模板实现也会导致这种情况发生,因为指针可能在错误的vector中。我可以明确说明类型,但我宁愿让编译器确定这个。所以我的问题是,有没有办法遍历继承树来找到对象?或者也许还有其他方法可以做到这一点?

我也不想在类中添加额外的函数和成员来观察。我已经想到了这一点,但是如果我能有一个更清晰的分离,我希望如此。

1 个答案:

答案 0 :(得分:0)

我没有机会看到@Igor给出的评论,但是昨晚我正在考虑这个问题,我认为无法确定正确的清单,所以我和#39;我没有打扰并声明用户必须知道要注意的类型。

我也想到了与@Igor所说的相同的错误,然后是一些错误。这是我正在使用的最终实现:

#pragma once
#include <memory>
#include <vector>
#include <algorithm>

#define _AFXDLL  // required if using the MFC DLL and not a static implementation
#include <afx.h> // used for the ASSERT() macro
// DeleteReporter
//
// DESCRIPTION
//   This class is to warn a function that at somepoint in its execution, the
//   object of interest has been deleted.
//
// USAGE
//   To use, add the function call MarkDeleted(this); to the end of the 
//   destructor of the type you wish to test for.
//
//   In the function that you want to check for the destruction, create a 
//   DeleteReporter<T> variable, where T is the type where you added the
//   MarkDeleted(this) to.
//
//   You can now query the object you created to determine if the object of
//   interest has been deleted.
//
//   Example:
//
//   C::~C()
//   {
//     ...
//
//     MarkDeleted(this);
//   }
//
//   void C::fn()
//   {
//      ...
//
//      DeleteReporter<C> C_Deleted(this);
//
//      ... do stuff ...
//
//      if (!C_Deleted)
//      {
//        ... call member functions ...
//      }
//
//      ...
//   }
//
//                                            By: Adrian Hawryluk January 2017
template <typename T>
void MarkDeleted(T* pObject);

template <typename T>
class DeleteReporter
{
    friend void MarkDeleted<T>(T* pObject);
    using        pair_t = std::pair<T*, bool>;
    using shared_pair_t = std::shared_ptr<pair_t>;
    struct vector_t;

    static vector_t obj_deleted_pairs;

    shared_pair_t obj_deleted_pair;
public:
    DeleteReporter(T* pObject);
    DeleteReporter(DeleteReporter const &)  = delete;
    DeleteReporter(DeleteReporter       &&) = delete;
    operator bool();
    ~DeleteReporter();
};




//////////////////////////////////////////////////////////////////////////////
// Implementation

template <typename T>
void MarkDeleted(T* pObject)
{
    using      vector_t = typename DeleteReporter<T>::vector_t;
    using shared_pair_t = typename DeleteReporter<T>::shared_pair_t;
    vector_t& obj_deleted_pairs = DeleteReporter<T>::obj_deleted_pairs;
    for (shared_pair_t& obj_deleted_pair : obj_deleted_pairs)
    {
        if (obj_deleted_pair->first == pObject)
        {
            obj_deleted_pair->second = true;
        }
    }
}

template <typename T>
struct DeleteReporter<T>::vector_t : std::vector<shared_pair_t>
{
    ~vector_t()
    {
        // When deleting the storage vector, it should be empty, or there's an error
        ASSERT(size() == 0);
    }
};

template <typename T>
typename DeleteReporter<T>::vector_t DeleteReporter<T>::obj_deleted_pairs;

template <typename T>
DeleteReporter<T>::DeleteReporter(T* pObject)
{
    obj_deleted_pair = std::make_shared<pair_t>(pObject, false);
    obj_deleted_pairs.emplace_back(obj_deleted_pair);
}

template <typename T>
DeleteReporter<T>::operator bool()
{
    return obj_deleted_pair->second;
}

template <typename T>
DeleteReporter<T>::~DeleteReporter()
{
    auto& it = std::find(obj_deleted_pairs.begin(), obj_deleted_pairs.end(), obj_deleted_pair);
    obj_deleted_pairs.erase(it);
}