2009-04-02 13 views
7

Vorrei creare in C++ una classe di Notifier che userò in altri oggetti per notificare vari titolari quando l'oggetto viene distrutto.Come posso conoscere l'indirizzo dell'oggetto proprietario in C++?

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; 
}; 

mio punto è che ho un oggetto grafico fitta e complicata, mi piacerebbe evitare di memorizzare l'indirizzo dell'oggetto di proprietà in notificante. C'è un modo per cambiare la mia classe di notificante in modo che possa dedurre l'indirizzo dell'oggetto di proprietà dal proprio indirizzo e un offset che verrebbe calcolato in fase di compilazione?

Si noti inoltre che qualsiasi oggetto potrebbe dover informare diversi "proprietari", possibilmente della stessa classe.

Grazie.

+0

È necessario il polimorfismo statico oppure è possibile creare un IOwner di classe di base astratto con ad es. un metodo virtuale puro 'notifica'? –

+0

No, non posso avere una "notifica" virtuale pura. –

risposta

2

O qualcosa di simile:

Eredita dal notificante e add Di proprietà come parametro di template.Poi si può avere un metodo di proprietà a disposizione all'interno del notificatore:

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. Questa è a mio parere la soluzione migliore. Tuttavia, non consente più proprietari dello stesso tipo. –

+0

@Luc: Sì, consente più proprietari dello stesso tipo con un po 'più di lavoro: aggiungendo per es. un parametro template int ... Vedi la mia risposta. –

+0

Questa soluzione è applicabile solo se il grafico dell'oggetto è quasi statico (non più di un oggetto dello stesso tipo). Se crei istanze dinamicamente in un modo ad albero, hai bisogno che le istanze del proprietario siano archiviate. – mmmmmmmm

1

Parte della soluzione è possedere ereditare da Notifier. In questo modo, l'indirizzo dell'oggetto distrutto è semplicemente 'questo' ...

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

Ma come gestire più 'proprietari' della stessa classe? Come si può ereditare più volte dalla "stessa classe"?

Grazie a fa's answer, ecco la soluzione che cercavo:

#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; 
} 

Grazie!

+0

Nel notificatore, non è possibile memorizzare un elenco di proprietari anziché uno singolo? –

+0

Il tuo caso d'uso ti consente davvero di sapere tutto questo al momento della compilazione? E stai bene con il codice gonfiarsi dall'istanziare così tanti modelli? – rmeador

+0

Il caso d'uso è quello di implementare le relazioni tra le classi in un modello di oggetti di business. Per esempio. una domanda è legata a un cliente: so tutto su quelle classi e so che ogni richiesta ha esattamente 1 cliente e che ogni cliente ha da 0 a n richieste ... –

6

Dai uno sguardo allo GoF Observer Design Patter.

+0

Sto davvero cercando un modo per implementare un relazione uno-a-molti. Può essere usato molto bene per un pattern Observer, ma non necessariamente. –

+0

@Xavier: non sono sicuro di averti capito. "molto bene essere usato per un osservatore". L'osservatore è il modo in cui è possibile implementare la notifica. –

+0

"L'Observer definisce una relazione uno-a-molti in modo che quando un oggetto cambia stato, gli altri vengono notificati e aggiornati automaticamente". Ma voglio che i molti lo notifichino (in modo efficiente in termini di memoria). –

0

Lo dubito fortemente. Non è possibile per il Notifier sapere che è stato utilizzato in composizione. Che cosa succede se faccio

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

mi piacerebbe essere smentito, però, ma ho davvero dubbi è fattibile senza dare esplicitamente ulteriori informazioni al Notifier.

+0

Qualsiasi modello (o altro) trucco per fornire queste informazioni in fase di compilazione? –

+0

Non esiste un modo in cui i modelli possano aiutarti qui. –

3

Sarebbe un hack brutta e probabilmente non è garantito il funzionamento, ma qui è un pensiero Non consiglio questo.

Supponiamo di avere il layout, come lei ha descritto in questo modo:

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; 
}; 

Se _notifier conosce il suo nome, si potrebbe calcolare 'indirizzo di s come questo (che viene eseguito nella Notifier' Owned costruttore s):

in pratica, l'ipotesi è che _notifier sia in qualche offset fisso all'interno della classe Owned. Pertanto l'indirizzo di proprietà è uguale all'indirizzo _notifier meno lo stesso offset.

Ancora una volta, si tratta di un comportamento indefinito che non consiglierei, ma che potrebbe funzionare.

3

fa.'s answer è un buon inizio. Tuttavia, non risolve il problema di avere più proprietari dello stesso tipo. Una soluzione è quella di far memorizzare al notificatore un elenco di proprietari anziché uno singolo. Ecco una rapida implementazione, per mostrare l'idea:

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; 
}; 

Si può usare in questo modo:

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()) { } 
}; 

Nel caso in cui si hanno molti diversi tipi di proprietari, questa soluzione può diventare piuttosto difficile usare. In questo caso, si potrebbe desiderare di guardare le librerie spinta metaprogrammazione (MPL, Fusion), con il quale si potrebbe finire con un codice che consentono di fare gli animali del genere:

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

Tuttavia, l'implementazione di questa soluzione sarebbe essere un po 'più lungo del precedente

+0

Questo è molto interessante se è necessario un elenco dinamico di proprietari. Nel mio caso, so in anticipo quanti proprietari ho di ogni tipo (di solito uno, a volte due, mai più). –

Problemi correlati