2010-09-30 17 views
5

Uno strumento di verifica statica mostra una violazione del codice qui sotto:Chi downcasting dalla classe base per creare una sottoclasse puntatore

class CSplitFrame : public CFrameWnd 
... 
class CVsApp : public CWinApp 
CWnd* CVsApp::GetSheetView(LPCSTR WindowText) 
{ 
CWnd* pWnd = reinterpret_cast<CSplitFrame*>(m_pMainWnd)->m_OutputBar.GetChildWnd(WindowText); 
return pWnd; 
} 

messaggio di errore: Class 'CSplitFrame' eredita dalla classe 'CWnd'

Descrizione: Evita la gerarchia dell'ereditarietà. Questa regola rileva i cast da un puntatore di classe base a un puntatore sottoclasse.

Vantaggi: La riduzione della gerarchia dell'ereditarietà causa problemi di manutenzione e il downcasting da una classe base è sempre illegale.

Riferimenti:

  1. Scott Meyers, "Effective C++: 50 modi specifici per migliorare i programmi e design", seconda edizione, Addison-Wesley, (C) 2005 Pearson Education, Inc., Capitolo : "eredità e progettazione object-oriented", punto 39
  2. Joint Strike Fighter, trasporto aereo, C++ standard di codifica capitolo 4.23 Conversioni di tipo, Regola AV 178

Pensi che sia una buona pratica non scartare da un puntatore di classe base a un puntatore sottoclasse? Perché e quando dovrei seguire questa regola?

+1

Esecuzione di un analizzatore statico sul codice MFC? Stai scherzando vero? Lo stesso MFC rompe molti standard di codifica JSF, il codice generato dalla procedura guidata si rompe di più, in realtà non è progettato per la sicurezza del tipo. –

+0

BTW: sarebbe una buona idea smettere di chiamare sottoclassi di classi derivate. La derivazione/ereditarietà è un concetto sintattico, la sottoclassi è un concetto di battitura, e le due idee sono più o meno indipendenti. – Yttrill

risposta

4

Andiamo attraverso alcuni dei esempio downcasting in MFC:

CButton * da CWnd *

CWnd* wnd = GetDlgItem(IDC_BUTTON_ID); 
CButton* btn = dynamic_cast<CButton*>(wnd); 

CChildWnd * da CFrameWnd *

CChildWnd * pChild = ((CSplitFrame*)(AfxGetApp()->m_pMainWnd))->GetActive(); 

ci sono infatti alcuni dei limitazione del design MFC.

causa CWnd fornisce le funzionalità di base di tutte le classi di finestra in MFC, lo fa anche servire come una classe di base di Vista, di dialogo, ecc Pulsante

Se vogliamo evitare downcasting, probabilmente abbiamo bisogno di hacker MFC dividere CWnd in meno pezzi?

Ora, giunge alla un'altra domanda, come risolvere la violazione, il mio modesto parere è cercare di evitare downcasting non sicuri, utilizzando downcasting sicura:

Parent *pParent = new Parent; 
Parent *pChild = new Child; 

Child *p1 = static_cast<Child*>(pParent); // Unsafe downcasting:it assigns the address of a base-class object (Parent) to a derived class (Child) pointer 
Parent *p2 = static_cast<Child*>(pChild); // Safe downcasting:it assigns the address of a derived-class object to a base-class pointer 

servire come buona pratica per l'utilizzo di downcasting sicuro, anche se la violazione è ancora esistente, sopprimeremo la violazione con una spiegazione data.

Pochi di riferimento utile:
http://support.microsoft.com/kb/108587
http://blog.csdn.net/ecai/archive/2004/06/26/27458.aspx
http://www.codeproject.com/KB/mcpp/castingbasics.aspx
http://www.bogotobogo.com/cplusplus/upcasting_downcasting.html

Infine, grazie per la risposta varia utile da tutti voi.
Sono davvero molto utili.

10

reinterpret_cast è certamente una cattiva idea qui, indipendentemente dagli standard di codifica o dalla teoria OOP. Deve essere dynamic_cast o boost::polymorphic_downcast.

Quanto al capitolo 39 del Effective C++, si concentra sui problemi di manutenzione causati da dover downcast a più tipi diversi e di dover controllare i valori restituiti dynamic_cast per potenziali errori, con conseguente rami multipli nel codice:

L'importante è questo: lo stile di programmazione if-then-else che il downcasting porta invariabilmente è di gran lunga inferiore all'uso di funzioni virtuali e dovresti riservarlo a situazioni in cui non hai davvero alternative.

+4

Oppure 'static_cast', se il tipo dinamico dell'oggetto è noto. –

+3

In realtà, quello che ho detto è fuorviante. 'static_cast (p)' può essere usato tranquillamente se '* p' è noto per essere un' T'. Perché sia ​​così, 'T' non deve essere necessariamente il tipo dinamico di' * p'. 'T' potrebbe anche essere una classe base del tipo dinamico di * * p' e una classe derivata del tipo statico di' * p'. 'T' non deve necessariamente essere il tipo dinamico di' * p'. –

0

Non mi sembra che sia effettivamente necessario per eseguire comunque il cast, o almeno non come lo si sta facendo. La tua funzione deve restituire un valore CWnd, quindi non è necessario eseguire il cast su un CSplitFrame.

Mi sarei aspettato che GetChildWnd restituisca un CWnd* o simile; perché si può non scrivere qualcosa di simile: i puntatori di classe

CWnd* CVsApp::GetSheetView(LPCSTR WindowText) 
{ 
    return m_pMainWnd->m_OutputBar.GetChildWnd(WindowText); 
} 
+1

m_pMainWnd è un membro di CWnd, che non ha accesso a m_OutputBar, che è un membro di CSplitFrame. – wengseng

0

downcasting è generalmente qualcosa che dovrebbe essere evitato a tutti i costi nel codice OOP correttamente progettato. Tuttavia, a volte tali cast sono necessari, specialmente se si utilizza qualche altra libreria/codice che è stato progettato per richiedere tali cast nel codice client. MFC è un esempio di tale libreria.

Quando i downcast sono realmente necessari, non dovrebbero mai essere eseguiti con reinterpret_cast.Il modo corretto di eseguire il cast è dynamic_cast o static_cast.

Molto spesso le persone usano reinterpret_cast quando vedono un cast di tipo C utilizzato per il downcast nel codice e decidono di convertirlo in cast in stile C++, assumendo erroneamente che un cast in stile C in tale contesto sia equivalente a reinterpret_cast. In realtà, il cast in stile C applicato alla coppia di tipi padre-figlio in qualsiasi direzione equivale a static_cast con alcuni vantaggi aggiuntivi (il cast in stile C può violare la protezione di accesso). Quindi, di nuovo, usare cast in stile C per questo scopo è sbagliato, ma la corretta sostituzione di C++ non è reinterpret_cast, ma piuttosto dynamic_cast o static_cast.

Problemi correlati