2009-04-02 5 views
7

개체를 소멸시킬 때 다양한 홀더에 알리기 위해 다른 개체에서 사용할 알리미 클래스를 C++로 만들고 싶습니다.C++에서 owner 객체의 주소를 어떻게 알 수 있습니까?

template <class Owner> 
class Notifier<Owner> { 
public: 
    Notifier(Owner* owner); 
    ~Notifier(); // Notifies the owner that an object is destroyed 
}; 

class Owner; 

class Owned { 
public: 
    Owned(Owner* owner); 
private: 
    Notifier<Owner> _notifier; 
}; 

내 요점은 내가 조밀하고 복잡한 객체 그래프를 가지고, 내가 신고자의 소유 객체의 주소를 저장하지 않으려는 것입니다. 자신의 주소에서 소유 객체의 주소와 컴파일 타임에 계산 된 오프셋을 추론 할 수 있도록 알리미 클래스를 변경하는 방법이 있습니까?

모든 개체는 동일한 클래스의 여러 '소유자'에게 알릴 수 있습니다.

감사합니다.

+0

정적 다형성이 필요합니까 아니면 예를 들어 기본 기본 클래스 IOwner를 만들 수 있습니까? 순수한 가상 메소드가 '통지'됩니까? –

+0

아니요, 순수한 가상 '알림'을 가질 수 없습니다. –

답변

2

또는이 같은 : 귀하의 통지에서

상속과는 템플릿 매개 변수로 소유 추가 할 수 있습니다.

template < class Owner , class Owned > 
class Notifier 
{ 
public: 
    Notifier(Owner* owner) 
    {} 

    Owned * owned() 
    { return static_cast< Owned * >(this); } 

    ~Notifier() 
    { 
     // notify owner with owned() 
    } 
}; 

class Owner 
{}; 

class Owned : public Notifier< Owner , Owned > 
{ 
public: 
    Owned(Owner * owner) : Notifier< Owner , Owned >(owner) 
    {} 
}; 
+0

+1. 그것은 제 생각에 가장 좋은 해결책입니다. 그러나 동일한 유형의 여러 소유자는 허용하지 않습니다. –

+0

@Luc : 예, 동일한 유형의 여러 소유자가 더 많은 작업을 할 수 있습니다. int 템플릿 매개 변수 ... 내 대답을 참조하십시오. –

+0

이 솔루션은 개체 그래프가 거의 정적 인 경우에만 사용할 수 있습니다 (동일한 유형의 개체가 둘 이상 없음). 트리와 같은 방식으로 인스턴스를 동적으로 생성하는 경우 소유자의 인스턴스를 저장해야합니다. – mmmmmmmm

1

솔루션의 일부가 소유권을 알리미에서 상속받는 것입니다. 이 방법으로, 파괴 된 객체의 주소는 단순히 '이'...

class Owned : public Notifier<Owner> { 
public: 
    Owned(Owner* owner) 
    : Notifier<Owner>(owner) 
    {} 
}; 

하지만 어떻게 같은 클래스에서 여러 '주인'을 처리하는 방법? 어떻게 '같은 계급'에서 여러 번 상속받을 수 있습니까? fa's answer

감사합니다, 여기에 내가 찾던 솔루션입니다 :

#include <iostream> 

template <class Owner, class Owned, int = 0> 
class Notifier { 
public: 
    Notifier(Owner* owner) 
    : _owner(owner) 
    {} 
    ~Notifier() { 
    _owner->remove(owned()); 
    } 
    Owned * owned(){ 
    return static_cast< Owned * >(this); 
    } 

private: 
    Owner* _owner; 
}; 

class Owner { 
public: 
    void remove(void* any) { 
    std::cout << any << std::endl; 
    } 
}; 

class Owned : public Notifier<Owner,Owned,1>, Notifier<Owner,Owned,2> { 
public: 
    Owned(Owner* owner1, Owner* owner2) 
    : Notifier<Owner,Owned,1>(owner1) 
    , Notifier<Owner,Owned,2>(owner2) 
    {} 
}; 

int main() { 
    std::cout << sizeof(Owned) << std::endl; 
    Owner owner1; 
    Owner owner2; 
    Owned owned(&owner1, &owner2); 
    std::cout << "Owned:" << (void*)&owned << std::endl << std::endl; 
} 

감사합니다!

+0

알리미에서 단 하나의 소유자 목록 대신 소유자 목록을 저장할 수 있습니까? –

+0

유스 케이스를 사용하면 실제로 컴파일 타임에이 모든 것을 알 수 있습니다. 그리고 많은 템플릿을 인스턴스화하는 코드가 부 풀리면 괜찮습니까? – rmeador

+0

사용 사례는 비즈니스 개체 모델의 클래스 간 관계를 구현하는 것입니다. 예 : 수요가 고객과 연결되어 있습니다. 그 수업에 관한 모든 것을 알고 있고, 각 수요마다 정확하게 1 명의 고객이 있으며 각 고객은 0에서 n까지의 수요가 있음을 알고 있습니다. –

6

GoF Observer Design Patter을 살펴보십시오.

+0

저는 실제로 일대 다 관계. 그것은 Observer 패턴을 위해 사용될 수 있지만 꼭 그런 것은 아닙니다. –

+0

@Xavier : 나는 당신을 이해하는지 모르겠습니다. "옵서버에게 아주 잘 사용될 것". 옵서버는 알림을 구현할 수있는 방법입니다. –

+0

"Observer는 일대 다 관계를 정의하여 한 객체가 상태를 변경하면 다른 객체가 자동으로 통지 및 업데이트되도록합니다." 그러나 많은 사람들이 그 사실을 메모리 효율적인 방법으로 알려주기를 바랍니다. –

0

매우 의심 스럽습니다. 알리미가 알리미가 구성에 사용되었음을 알 수있는 방법이 없습니다. 내가

class Foo 
{ 
private: 
    Notifier _a, _b, _c; 
} 

을 할 경우 그래도 난 잘못 입증 싶지만, 정말 명시 적 알리미 더 많은 정보를 제공하지 않아도 해 드리겠습니다 의심한다.

+0

컴파일 타임에이 정보를 제공하기위한 템플릿 (또는 기타) 트릭이 있습니까? –

+0

여기 템플릿이 도움이되는 방법은 없습니다. –

3

IT는 불쾌한 해킹 아마도 작동을 보장하지만, 여기에 내가이하지 않는 것이 좋습니다 생각 입니다하지 않을 것입니다. _notifier 그 이름을 알고

template <class Owner> 
class Notifier<Owner> { 
public: 
    Notifier(Owner* owner); 
    ~Notifier(); // Notifies the owner that an object is destroyed 
}; 

class Owner; 

class Owned { 
public: 
    Owned(Owner* owner); 
private: 
    Notifier<Owner> _notifier; 
}; 

경우는 '(생성자 OwnedNotifier에서 실행)이 같은의 주소를'계산 수 :

는 다음과 같이 설명처럼 당신이 당신의 레이아웃이 있다고 가정

Owned *p = reinterpret_cast<Owned *>(reinterpret_cast<char *>(this) - offsetof(Owned, _notifier)); 

기본적으로 _notifier는 Owned 클래스 내의 고정 오프셋에 있습니다. 따라서 Owned의 주소는 _notifier의 주소에서 동일한 오프셋을 뺀 것과 같습니다.

다시 한번 말하지만 이것은 권장하지 않지만 작동하지 않을 수있는 정의되지 않은 동작입니다.

3

fa.'s answer 좋은 시작이다 : 그럼 당신은 신고자 내부 사용할 수있는 소유의 방법이있을 수 있습니다. 그러나 동일한 유형의 소유자가 여러 명있는 문제는 해결되지 않습니다. 한 가지 해결책은 통보자가 단일 목록 대신 소유자 목록을 저장하도록하는 것입니다.

template <typename Owner, typename Owned> 
class Notifier 
{ 
    protected: 
    Notifier() 
    {} 

    // Constructor taking a single owner 
    Notifier(Owner & o) 
    { 
     owners.push_back(&o); 
    } 

    // Constructor taking a range of owners 
    template <typename InputIterator> 
    Notifier(InputIterator firstOwner, InputIterator lastOwner) 
     : owners(firstOwner, lastOwner) {} 

    ~Notifier() 
    { 
     OwnerList::const_iterator it = owners.begin(); 
     OwnerList::const_iterator end = owners.end(); 
     for (; it != end ; ++it) 
     { 
      (*it)->notify(static_cast<Owned*>(this)); 
     } 
    } 

    // Method for adding a new owner 
    void addOwner(Owner & o) 
    { 
     owners.push_back(&o); 
    } 

private: 
    typedef std::vector<Owner *> OwnerList; 
    OwnerList owners; 
}; 

당신이 이런 식으로 사용할 수 있습니다 : 여기에 빠른 구현이 아이디어를 보여주고, 당신이 소유자의 많은 다른 유형이있는 경우

class Owner; 

class Owned : public Notifier<Owner, Owned> 
{ 
    typedef Notifier<Owner, Owned> base; 

    //Some possible constructors: 
    Owned(Owner & o) : base(o) { } 

    Owned(Owner & o1, Owner & o2) 
    { 
     base::addOwner(o1); //qualified call of base::addOwner 
     base::addOwner(o2); //in case there are other bases 
    } 

    Owned(std::list<Owner*> lo) : base(lo.begin(), lo.end()) { } 
}; 

을,이 솔루션은 오히려 어려워 질 수 있습니다 사용. 것이 솔루션을 구현, 그러나

class Owned : public Notifier<Owned, OwnerType1, OwnerType1, OwnerType2> 
{ 
    Owned(OwnerType1 & o1, OwnerType1 & o2, OwnerType2 & o3) 
     : base(o1,o2,o3) 
}; 

:이 경우, 당신은 당신이 그런 식으로 물건을 할 수 있도록 코드로 끝낼 수있는 부스트 메타 프로그래밍 라이브러리 (MPL, Fusion),보고 할 수 있습니다 이전보다 조금 길어.

+0

동적 소유자 목록이 필요한 경우 매우 흥미 롭습니다. 내 경우에는 각 유형에 대해 얼마나 많은 소유자가 있는지 미리 알 수 있습니다 (일반적으로 하나, 때로는 두 가지, 그 이상은 아님). –

관련 문제