2011-10-26 15 views
32

Mentre indago sul codice sorgente di Qt ho visto che i ragazzi del trolltech utilizzano esplicitamente la parola chiave this per accedere a un campo sul distruttore.In una classe derivata basata su modelli, perché è necessario qualificare i nomi dei membri della classe base con "this->" all'interno di una funzione membro?

inline ~QScopedPointer() 
{ 
    T *oldD = this->d; 
    Cleanup::cleanup(oldD); 
    this->d = 0; 
} 

Quindi, qual è il punto di questo utilizzo? Ci sono dei benefici?

Edit: Per coloro che votano per la chiusura di questa domanda, ho il sospetto che questo utilizzo è per alcuni casi di ereditarietà di classe

Una parte di QScopedPointer class definizione:

template <typename T, typename Cleanup = QScopedPointerDeleter<T> > 
class QScopedPointer 

risposta

39

risposta C++ (risposta generale)

consideri una classe template Derived con una classe di base template:

template <typename T> 
class Base { 
public: 
    int d; 
}; 

template <typename T> 
class Derived : public Base<T> { 
    void f() { 
     this->d = 0; 
    } 
}; 

this ha tipo Derived<T>, un tipo che dipende da T. Quindi this ha un tipo dipendente. Quindi this->d rende d un nome dipendente. I nomi dipendenti vengono cercati nel contesto della definizione del modello come nomi non dipendenti e nel contesto dell'istanziazione.

Senza this->, il nome d viene cercato solo come nome non dipendente e non viene trovato.

Un'altra soluzione è quella di dichiarare d nella definizione modello stesso:

template <typename T> 
class Derived : public Base<T> { 
    using Base::d; 
    void f() { 
     d = 0; 
    } 
}; 

Qanswer (risposta specifica)

d è un member of QScopedPointer. Non è un membro ereditato. this-> non è necessario qui.

OTOH, QScopedArrayPointer is a template class and d is an inherited member of a template base class:

template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> > 
class QScopedArrayPointer : public QScopedPointer<T, Cleanup> 

così this-> occorre here:

inline T &operator[](int i) 
{ 
    return this->d[i]; 
} 

E 'facile vedere che è più facile appena messo this-> ovunque.

capire il motivo

Credo che non è chiaro a tutti gli utenti di C++ per cui nomi sono guardato-up di classi base non-dipendenti, ma non in classi base dipendenti:

class Base0 { 
public: 
    int nd; 
}; 

template <typename T> 
class Derived2 : 
     public Base0, // non-dependent base 
     public Base<T> { // dependent base 
    void f() { 
     nd; // Base0::b 
     d; // lookup of "d" finds nothing 

     f (this); // lookup of "f" finds nothing 
        // will find "f" later 
    } 
}; 

C'è un motivo accanto a "lo standard dice così": causa del nome modo vincolante nei modelli funziona.

I modelli possono avere nome che sono legati in ritardo, quando il modello viene creata un'istanza: ad esempio f in f (this). Al punto della definizione di Derived2::f(), non esiste alcuna variabile, nome di funzione o tipo f nota al compilatore.L'insieme di entità note a cui potrebbe fare riferimento f è vuoto. Questo non è un problema perché il compilatore sa che cercherà in seguito f come nome di funzione o nome di una funzione modello.

OTOH, il compilatore non sa cosa fare con d; non è un nome di funzione (chiamato). Non esiste un modo per legare in ritardo nomi di funzioni non (chiamati).

Ora, tutto questo può sembrare conoscenza elementare della fase di compilazione del modello polimorfismo. La vera domanda sembra essere: perché lo d non è legato allo Base<T>::d al momento della definizione del modello?

Il vero problema è che non c'è Base<T>::d in fase di definizione del modello, perché non v'è alcun tipo completo Base<T> in quel momento: Base<T> è dichiarato, ma non definito! Ci si può chiedere: che dire di questo:

template <typename T> 
class Base { 
public: 
    int d; 
}; 

sembra che la definizione di un tipo completo!

In realtà, fino a quando esemplificazione, sembra più simile a:

template <typename T> 
class Base; 

al compilatore. Un nome non può essere cercato in un modello di classe! Ma solo in una specializzazione template (istanziazione). Il modello è una fabbrica per la specializzazione dei modelli, un modello non è un set di specializzazione modello. Il compilatore può cercare d in Base<T> per qualsiasi tipo particolare T, ma non può cercare d nel modello di classe Base. Fino a quando viene determinato un tipo T, Base<T>::d rimane l'abstract Base<T>::d; Solo quando il tipo T è noto, Base<T>::d iniziare riferirsi ad una variabile di tipo int.

La conseguenza di ciò è che il modello di classe Derived2 ha una base completa di classe Base0 ma una incompleta (in avanti dichiarata) classe base Base. Solo per un tipo noto T, la "classe template" (specializzazioni di un modello di classe) Derived2<T> ha un classi di base completo, proprio come qualsiasi classe normale.

Ora vediamo che:

template <typename T> 
class Derived : public Base<T> 

è in realtà un base di template specifica classe (una fabbrica per fare le specifiche della classe base) che segue regole diverse da una specifica classe di base all'interno di un modello.

Nota: Il lettore potrebbe aver notato che ho composto alcune frasi alla fine della spiegazione.

Questo è molto diverso: qui d è un nome qualificato Derived<T>, e Derived<T> dipende dal T è un parametro di modello. Un nome qualificato può essere tardivo anche se non è un nome di funzione (chiamato).

Un'altra soluzione è:

template <typename T> 
class Derived : public Base<T> { 
    void f() { 
     Derived::d = 0; // qualified name 
    } 
}; 

Questo è equivalente.

Se pensi che all'interno della definizione di Derived<T>, il trattamento di Derived<T> come una classe completa nota a volte e come una classe sconosciuta altre volte incoerente, beh, hai ragione.

+0

** Nota del moderatore ** _I commenti sotto questa risposta sono stati eliminati perché sono degenerati in rumore puro. Per favore usa [chat] per discussioni estese e ricorda di rimanere civile._ –

1

Suppongo che si tratti di un uso eccessivo della routine Cleanup(). Il tipo che viene passato è esplicitamente controllato dal tipo di modello T, che a sua volta può controllare quale versione di Cleanup() sovraccaricata viene invocata.

Problemi correlati