Memorizzazione di un parametro pass-by-reference come puntatore - Cattiva pratica?

4

Recentemente ho trovato il seguente pattern in un'API che sono stato forzato a utilizzare:

class SomeObject
{
public:
    // Constructor.
    SomeObject(bool copy = false);

    // Set a value.
    void SetValue(const ComplexType &value);

private:
    bool         m_copy;
    ComplexType *m_pComplexType;
    ComplexType  m_complexType;
};

// ------------------------------------------------------------

SomeObject::SomeObject(bool copy) :
  m_copy(copy)
{
}

// ------------------------------------------------------------

void SomeObject::SetValue(const ComplexType &value)
{
    if (m_copy)
        m_complexType.assign(value);
    else
        m_pComplexType = const_cast<ComplexType *>(&value);
}

Lo sfondo di questo pattern è che viene utilizzato per contenere i dati prima che vengano codificati e inviati a un socket TCP. La stranezza della copia è progettata per rendere efficiente la classe SomeObject tenendo solo un puntatore all'oggetto finché non ha bisogno di essere codificato, ma fornisce anche l'opzione per copiare i valori se la durata del SomeObject supera la durata di un ComplexType .

Tuttavia, considera quanto segue:

SomeObject SomeFunction()
{
    ComplexType complexTypeInstance(1);  // Create an instance of ComplexType.

    SomeObject encodeHelper;
    encodeHelper.SetValue(complexTypeInstance); // Okay.

    return encodeHelper;

    // Uh oh! complexTypeInstance has been destroyed, and
    // now encoding will venture into the realm of undefined
    // behaviour!
}

Sono inciampato su questo perché ho usato il costruttore di default, e questo ha comportato la codifica dei messaggi come vuoti (attraverso una combinazione di comportamento indefinito). Ci è voluta un'età assoluta per individuare la causa!

Ad ogni modo, questo è uno schema standard per qualcosa di simile? Ci sono dei vantaggi nel fare ciò in questo modo? Sovraccarico del metodo SetValue per accettare un puntatore che mi manca?

Grazie!

    
posta Karl Nicoll 23.10.2013 - 01:03
fonte

2 risposte

2

Direi di sì è standard avere un riferimento che viene preso come un puntatore (come in Ho visto in più progetti) e sì è cattivo in quanto nasconde il problema della durata dell'oggetto.

E il cast const è inutile e completamente orribile. Per lo meno dovrebbe essere un ref passato, non un const ref.

Come suggerisci un modo per migliorarlo sarebbe meglio avere 2 diverse funzioni una che prende un const ref e una copia (impostando il flag di copia nello stesso momento), e l'altra che prende un puntatore, quindi l'utente è costretto a chiamare esplicitamente il comportamento non di copia.

Sarebbe meglio avere 2 metodi diversi con nomi diversi (forse CopyValue e ReferenceValue), e sarebbe meglio avere una classe base con 2 classi di classi secondarie diverse che eseguono 2 comportamenti diversi.

    
risposta data 23.10.2013 - 01:48
fonte
1

Un'altra soluzione sarebbe quella di sostituire il metodo che prende un riferimento con uno che accetta un puntatore. Il chiamante creerebbe quindi l'oggetto usando l'operatore new e lo memorizzerebbe in un unique_ptr (direi auto_ptr, ma il motivo per cui è stato deprecato è ciò che finiremmo usando qui). In questo modo, non fai copie, né ti preoccupi della distruzione dell'oggetto.

Un mash-up veloce sarebbe

class SomeObject
{
public:
    // Constructor.
    SomeObject();

    // Set a value.
    void SetValue(unique_ptr<ComplexType> pValue);

private:
    std::unique_ptr<ComplexType> m_pComplexType;
};

// ------------------------------------------------------------

void SomeObject::SetValue(std::unique_ptr<ComplexType> pValue)
{
    m_pComplexType = std::move(pValue);
}

.. che può essere usato come:

SomeObject SomeFunction()
{
    std::unique_ptr<ComplexType> pobjComplexTypeInstance= new ComplexType(1);  // Create an instance of ComplexType and store it in a unique_ptr

    SomeObject encodeHelper;
    encodeHelper.SetValue(pobjComplexTypeInstance); // Pass the unique_ptr, it will be copied inside the function

    return encodeHelper;
}
    
risposta data 16.11.2013 - 20:19
fonte

Leggi altre domande sui tag