2017-01-19 4 views
1

기능 수준에서 삭제되는 개체를 관찰하고 싶습니다. 다시 말해, 함수의 내부에서 그 객체를 관찰하고 내가 삭제되었는지 여부를 알기 위해 무언가를 조사하도록 허용합니다. 기능이 완료되면 더 이상 관찰하지 마십시오.특정 클래스에 바인딩하지 않고 관찰자 패턴을 구현하는 방법은 무엇입니까?

이 내 현재의 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)); 
} 

, 그것은 전달 thisMarkDeleted()에 소멸자의 호출이있을 것입니다. 그런 다음 함수에서 DeleteReporter을 인스턴스화하여 make_DeleteReporter()을 사용하여 관찰 할 객체를 전달합니다. 나중에 DeleteReporter 개체를 쿼리하여 개체가 인스턴스화 후에 언젠가 삭제되지 않도록 보장합니다.

원래 템플릿으로 사용하지는 않았지만 그 대신 함수는 void*을 사용했습니다. 그런 다음 개체가 여러 번 상속 된 경우 포인터가 제대로 일치하지 않을 수도 있음을 알았습니다.

필자가 한 것처럼 템플릿을 사용하여 구현하면 포인터가 잘못된 vector에있을 수도 있습니다. 구체적으로 타입을 명시 할 수는 있지만, 컴파일러가이를 결정하게합니다. 그래서 내 질문은 객체를 찾기 위해 상속 트리를 가로 지르는 방법이 있을까요? 아니면 다른 방법으로이 작업을 수행 할 수 있습니까?

나는 또한 관찰 할 클래스에 추가 기능과 멤버를 추가 할 필요가 없다. 나는 그것의 생각을 가지고 있지만, 내가 더 깨끗한 분리를 할 수 있다면 그것을 원할 것이다.

+0

'obj_deleted_pair = * obj_deleted_pairs () .rbegin();'위험, 윌 로빈슨! 벡터에 추가하면 모든 포인터, 참조 및 반복기가 벡터의 기존 요소로 무효화 될 수 있습니다. 'DeleteReporter'의 새로운 인스턴스를 생성하면 다른 모든 살아있는 인스턴스는 매달려있는 포인터를 가질 수 있습니다. –

+0

또한 개체가 만들어지고 파괴 된 다음 다른 개체가 동일한 주소로 만들어지고 (결국 파괴 될 수도 있음) 가능할 수 있습니다. 이 경우, 벡터에 같은 포인터를 가진 두 쌍을 갖게되지만,'MarkDeleted'는 첫 번째 것을 두 번 업데이트합니다. –

+0

벡터에서 지우기 ('~ DeleteReporter'는)는 지워진 요소와 그 뒤의 모든 요소에 대한 모든 포인터 참조 및 반복자를 무효화합니다. –

답변

0

@Igor이 지금까지 준 의견을 볼 기회가 없었지만 지난 밤에 나는 이것에 대해 생각하고 있었고 정확한 목록을 결정할 수 없다고 생각 했으므로 귀찮아서 사용자가 볼 유형을 알아야한다는 사실

나는 또한 @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); 
}