2014-10-30 7 views
7

Recentemente ho scoperto una nuova applicazione della parola chiave using; non con riferimento alla funzionalità namespace ma all'interno di una dichiarazione di classe derivata. Nel mio caso questo era pertinente per quanto riguarda i problemi che circondano la funzione membro 'operator ='.Svantaggi della parola chiave "Utilizzo" in C++ applicata alle classi derivate

ho avuto una situazione in cui, date le dichiarazioni:

class CString{ 
public: 
    ...//Various functions. 
    operator=(const CString &cString) 
    { 
     //code to share internal array and increment reference count 
    } 
    operator=(const wchar_t *pstrString) 
    { 
     //code to create a personal internal array, 
     //resize it appropriately and copy the pstrString argument. 
    } 
    ... 
}; 

class CStringEx : public CString{ 
    ...//various members that expand upon CString's abilities. 
}; 

... un oggetto di CStringEx non ha funzionato come mi aspettavo:

CStringEx cString; 

cString=L"String contents"; 

Invece un errore di compilazione è stato generato affermando ' CStringEx non ha una funzione 'operator =()' che accetta un argomento di tipo wchar_t * '(o - molto chiuso - parole in tal senso). Dopo un bel po 'di studio ho imparato che questo è dovuto al fatto che anche le funzioni membro generate automaticamente dal compilatore operator= di una classe derivata sovrascrivono quelle ereditate dalla sua classe genitore. Questo sembra contro-intuitivo e user-UN amichevole per me.

Tuttavia, se aggiungo una parola chiave using:

class CStringEx : public CString{ 
public: 
    using CString::operator=; 
    ... 
}; 

... il bambino della classe sarà ora utilizzare la funzione operator= membro del suo genitore e tutto va bene.

Finora, tutto bene. Tuttavia, dopo ulteriori letture qui e altrove ho appreso che molti programmatori non amano utilizzare using per questo scopo. Ad esempio, ho letto alcuni commentatori che descrivono effetti collaterali potenzialmente indesiderati, come il branding in TUTTO l'operatore = dal genitore. Tuttavia, ancora una volta, a parte circostanze molto specifiche, non capisco perché l'ereditarietà di tutte le funzioni dei membri del genitore sia e sia problematica. Se questa è la preoccupazione principale, qualcuno potrebbe spiegare i pericoli generali di farlo?

L'unica alternativa che posso pensare è quello di scrivere le funzioni stub nella classe bambino per ognioperator= membro funzione del suo genitore e quindi chiamare in modo esplicito quei rispettivi Stati-funzioni:

class CStringEx : public CString{ 
public: 
    ... 
    const CStringEx& operator=(const wchar_t* pstrString) 
    { 
     CString::operator=(pstrString); 
     return *this; 
    } 
    const CStringEx& operator=(const CString &cString) 
    { 
     CString::operator=(cString); 
     return *this; 
    } 
    ...//and so on... 
}; 

Quando rispetto alla versione con using CString::operator= questo sembra estremamente brutto, ingombrante e disordinato per me. Quindi, ancora una volta, perché non utilizzare la parola chiave using?

+5

"Ho appreso che molti programmatori non amano utilizzare utilizzando per questo scopo." Senza senso. Se è lo strumento giusto, è lo strumento giusto. –

+0

"Non capisco perché qualcuno potrebbe spiegare i pericoli di farlo?" Presumibilmente qualunque ragionamento che hai lasciato fuori dalla tua domanda potrebbe alludere a questo. –

+0

Woah ... Questo è un po 'di ascesa rimediabile! Ma va bene; intendi il ragionamento di coloro a cui non piace usare __using__? Se è così allora, per esempio [link] http://stackoverflow.com/questions/4122214/why-operator-doesnt-get-inherited-from-a-template-class [/ link] e _David Rodríguez_ comments. Chiarire; Non capisco perché la situazione che descrive sia "cattiva" e una ragione per usare invece l'approccio alla funzione stub. Ci sono altri esempi di scoraggiamento simile per _using_ in quel post e in altri. –

risposta

1

Questo è un po 'soggettivo quindi cerchiamo di rivedere quello che sappiamo:

Se using è lo strumento giusto per il lavoro, allora si dovrebbe usarlo. Detto questo:

  • using sarà sempre portare in tutte i metodi genitore/operatori, anche quelli aggiunti di recente che non hai mai previsto. Questo potrebbe anche causare codice non corretto quando qualcuno crea un operatore di assegnazione padre che non interagisce bene con l'oggetto figlio.
  • Portare l'operatore di assegnazione copie del genitore significa che è possibile creare un CStringEx da un CString ma forse questo è il comportamento previsto.
  • I futuri lettori/manutentori potrebbero non avere familiarità con l'operatore che utilizza la sintassi e potrebbe rendere il codice leggermente più difficile da eliminare.

Dato che lo stai interrogando in origine (in base alle cose che hai sentito) e ai punti sopra, facciamo un passo indietro e guardiamo il tuo design un momento. Il CStringEx ha due possibilità:

  1. Ha membri dati aggiuntivi. In questo caso sono disposto ad affermare che l'ereditarietà degli operatori padre sta andando a fare qualcosa di sbagliato in alcuni o in tutti i casi, dal momento che i membri della classe figlio non saranno gestiti nell'assegnazione.
  2. Non ha membri dati aggiuntivi, fornisce solo funzionalità di codice aggiuntive. In questo caso, per favore non essere tentato di usare una classe figlia. Invece scrivere algoritmi a funzione libera che operano su intervalli (iteratore) o, se necessario, su oggetti CString.
+0

** MOLTO **, molto ben messo Marco !!! Sono un po 'a disagio con l'idea di usare funzioni esterne piuttosto che una classe di bambini - ma penso di capire i pericoli che descrivi. Quello che descrivi sembra molto simile a come le funzioni di AWL di Hewlett Packard funzionano con algoritmi esterni e così via che "agganciano" a e mesh con un numero di tipi di oggetti diversi. Non ho ancora imparato o familiarizzato con queste nuove idee, ma è sicuramente qualcosa da guardare in futuro. Ho comunque scritto un iteratore in modo che la stringa funzioni con il nuovo ciclo for for (:) '. –