Domanda

Mi piacerebbe creare in C ++ una classe Notifier che userò in altri oggetti di notificare vari titolari quando l'oggetto viene distrutta.

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

Il mio punto è che, come 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 notificante in modo che possa dedurre l'indirizzo di un oggetto posseduto dal proprio indirizzo e un offset che sarebbe stato calcolato al momento della compilazione?

Si noti inoltre che qualsiasi oggetto può avere per notificare diversi 'proprietari', forse dalla stessa classe.

Grazie.

È stato utile?

Soluzione

O qualcosa di simile a questo:

Eredita dal notificante e aggiungere proprietà come parametro di template. Poi si può avere un metodo di proprietà disponibile 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 )
    {}
};

Altri suggerimenti

Date un'occhiata alla GoF Design Observer picchiettio .

Sarebbe un brutto Hack e probabilmente non garantito per funzionare, 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 l'indirizzo di Owned come questo (che viene eseguito nel costruttore del Notifier):

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

In sostanza, l'ipotesi è che _notifier è ad un certo offset fisso all'interno della classe di proprietà. Pertanto l'indirizzo della proprietà è pari a meno di indirizzo _notifier lo stesso compensato.

Ancora una volta, questo è un comportamento indefinito, che io non lo consiglio, ma poteva funzionare.

fa '. s risposta è un buon inizio. Tuttavia, non risolve il problema di avere più proprietari dello stesso tipo. Una soluzione è quella di avere il negozio notificatore un elenco di proprietari, invece di uno solo. 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 da usare. In questo caso, si potrebbe desiderare di guardare le librerie Boost 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 un po 'più lungo di quello precedente.

Una parte della soluzione sarebbe quella di avere proprietà 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 multipli 'proprietari' della stessa classe? Come si può ereditare più volte dalla 'stessa classe'?

Grazie alla Fa del rispondere , 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!

ne dubito fortemente. Non v'è alcun modo per il Notifier di 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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top