Domanda

Qual è l'approccio migliore per incapsulare oggetti e gestirne la durata? Esempio: ho una classe A , che contiene un oggetto di tipo B ed è l'unico responsabile .

Soluzione 1 , clona b per garantire che solo A sia in grado di ripulirlo.

class A
{
    B *b;
public:
    A(B &b)
    {
        this->b = b.clone();
    }

    ~A()
    {
        delete b; // safe
    }
};

Soluzione 2 , usa direttamente l'oggetto passato, qui rischiamo un potenziale doppio libero.

class A
{
    B *b;
public:
    A(B *b)
    {
        this->b = b;
    }

    ~A()
    {
        delete b; // unsafe
    }
};

Nel mio caso reale, la soluzione n. 2 si adatterebbe meglio. Tuttavia mi chiedo se questo sia considerato un codice errato perché qualcuno potrebbe non conoscere il comportamento di A , anche se è documentato. Posso pensare a questi scenari:

B *myB = new B();
A *myA = new A(myB);
delete myB; // myA contains a wild pointer now

In alternativa,

B *myB = new B();
A *firstA = new A(myB);
A *secondA = new A(myB); // bug! double assignment
delete firstA; // deletes myB, secondA contains a wild pointer now
delete secondA; // deletes myB again, double free

Posso semplicemente ignorare questi problemi se documento correttamente il comportamento di A? È sufficiente dichiarare la responsabilità e lasciare agli altri la lettura dei documenti? Come viene gestito nel tuo codebase?

È stato utile?

Soluzione

È necessario definire l'oggetto in modo che la semantica della proprietà sia, per quanto possibile, definita dall'interfaccia. Come ha sottolineato David Thornley, std :: auto_ptr è il puntatore intelligente di scelta per indicare trasferimento di proprietà . Definisci la tua classe in questo modo:

class A
{    
    std::auto_ptr<B> b;
public:    
    A(std::auto_ptr<B> b)    
    {
        this->b = b;
    }
    // Don't need to define this for this scenario
    //~A()
    //{ 
    //   delete b; // safe
    //}
};

Poiché il contratto di std :: auto_ptr prevede l'assegnazione = trasferimento di proprietà, il costruttore ora afferma implicitamente che un oggetto A ha la proprietà del puntatore su B che è passato. Infatti, se un client prova a fare qualcosa con uno std :: auto_ptr & Lt; B & Gt; che hanno usato per costruire una A dopo la costruzione, l'operazione fallirà, poiché il puntatore che detengono non sarà valido.

Altri suggerimenti

Non elimino mai nulla da solo a meno che non sia necessario. Ciò porta a errori.

I puntatori intelligenti sono i tuoi amici. std::auto_ptr<> è tuo amico quando un oggetto ne possiede un altro ed è responsabile dell'eliminazione quando esce dall'ambito. boost::shared_ptr<> (o, ora, std::tr1::shared_ptr<>) è tuo amico quando è potenzialmente più di un oggetto collegato a un altro oggetto e vuoi che l'oggetto venga eliminato quando non ci sono più riferimenti ad esso.

Quindi, utilizzare la soluzione 1 con auto_ptr o la soluzione 2 con shared_ptr.

Se stai scrivendo codice che qualcun altro utilizzerà in seguito, questi problemi devono essere risolti. In questo caso andrei per un semplice conteggio dei riferimenti (forse con i puntatori intelligenti). Considera il seguente esempio:

Quando un'istanza della classe incapsulante viene assegnata a un oggetto B, chiama un metodo per aumentare il contatore di riferimento B dell'oggetto. Quando la classe incapsulante viene distrutta, non elimina B, ma chiama invece un metodo per ridurre il conteggio dei riferimenti. Quando il contatore raggiunge lo zero, l'oggetto B viene distrutto (o si distrugge per quella materia). In questo modo più istanze di classe incapsulante possono funzionare con una singola istanza dell'oggetto B.

Ulteriori informazioni sull'argomento: Conteggio dei riferimenti .

Se il tuo oggetto è l'unico responsabile dell'oggetto passato, la sua eliminazione dovrebbe essere sicura. Se non è sicuro l'affermazione che sei l'unico responsabile è falso. Quindi che cos'è? Se la tua interfaccia è documentata che eliminerai l'oggetto in entrata, è responsabilità del chiamante assicurarsi di ricevere un oggetto che deve essere eliminato da te.

Se stai clonando A, e sia A1 che A2 mantengono riferimenti a B, la durata di B non è controllata interamente da A. È condivisa tra i vari A. La clonazione B assicura una relazione uno a uno tra As e Bs, che sarà facile garantire la coerenza nel corso della vita.

Se la clonazione di B non è un'opzione, è necessario scartare il concetto che A è responsabile della vita di B. O un altro oggetto dovrà gestire le varie B, oppure dovrai implementare un metodo come il conteggio dei riferimenti.

Per riferimento, quando penso al termine 'Clone', implica una copia profonda, che clonerebbe anche B. Mi aspetto che i due As siano completamente separati l'uno dall'altro dopo un clone.

Non clonare cose inutilmente o " solo per sicurezza " ;.

Invece so di chi è la responsabilità di eliminare qualcosa: tramite la documentazione o tramite puntatori intelligenti ... per esempio, se ho una funzione create che istanzia qualcosa e restituisce un puntatore ad essa e non elimina in modo che non sia chiaro dove e da chi dovrebbe mai essere cancellata quella cosa, quindi invece di <=> restituendo un puntatore nudo potrei definire il tipo di ritorno di <=> come restituire il puntatore contenuto in un qualche tipo di puntatore intelligente.

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