2011-07-22 7 views
15

Sono un programmatore C++ principiante, ma pensavo di conoscere abbastanza C++ fino ad oggi quando mi sono imbattuto in codice come questo al lavoro e non ho capito come funziona effettivamente .Modello Crazy C++ - Un modello per accedere ai singoli attributi di una classe

class Object 
{ 
}; 

template < 
     class PropObject, 
     class PropType, 
     PropType PropObject::* Prop 
     > 
class PropReader 
{ 
public: 
    void print(Object& o) 
    { 
     PropObject& po = static_cast<PropObject &>(o); 
     PropType& t = po.*Prop; 

     cout << t << "\n"; 
    } 
}; 

class Student : public Object 
{ 
public: 
    int age; 
    int grade; 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Student s; 
    s.age = 10; 
    s.grade = 5; 

    PropReader<Student, int, &Student::age> r; 
    PropReader<Student, int, &Student::grade> r2; 

    r.print(s); 
    r2.print(s); 
} 

Penso di averlo compreso a un livello elevato. Ma questo particolare PropType PropObject::* Prop nella dichiarazione del modello mi infastidisce. Cosa significa? Sto cercando una spiegazione dagli esperti di C++. Mi piacerebbe capirlo, così da poterlo usare meglio. Sembra molto utile però.

risposta

18

I modelli C++ sono noti per l'utilizzo di tipi come argomenti, ma possono anche essere parametrizzati su altri tipi di dati. Ad esempio, è possibile templatize una classe sopra un intero, come illustrato di seguito:

template <typename T, unsigned int N> class Array { 
private: 
    T array[N]; 

public: 
    /* ... */ 
}; 

I modelli possono anche essere parametrizzati su puntatori, fino a quando il puntatore soddisfa determinati criteri (ad esempio, si deve valutare a un indirizzo di qualcosa che può essere determinato in fase di compilazione).Ad esempio, questo è perfettamente legale:

template <int* Pointer> class ThisIsLegal { 
public: 
    void doSomething() { 
     *Pointer = 137; 
    } 
}; 

Nel codice, il modello è parametrizzato nel corso di un puntatore-a-class-membro. Un membro pointer-to-class è simile a un puntatore in quanto indirettamente si riferisce ad un oggetto. Tuttavia, invece di puntare a un oggetto, punta invece a un campo in una classe. L'idea è che è possibile dereferenziare un membro puntatore-a-classe relativo a qualche oggetto per selezionare quel campo fuori dalla classe. Ecco un semplice esempio di puntatori-to-Class-membro:

struct MyStruct { 
    int x, y; 
}; 

int main() { 
    MyStruct ms; 
    ms.x = 137; 
    ms.y = 42; 

    int MyStruct::* ptr; // Declare a pointer to a class member. 
    ptr = &MyStruct::x; // Now points to the field 'x' 

    ms.*ptr = 0;   // Set the 'x' field of ms to be zero. 
} 

Si noti che la sintassi per dichiarare un puntatore-a-class-membro è

Type ContainingClass::* pointerName; 

Così nel codice qui sopra, int MyStruct::* ptr mezzi . "un puntatore ad un int all'interno di una classe di MyStruct

Nel codice che hai postato, la dichiarazione modello di legge come questo:

template < 
    class PropObject, 
    class PropType, 
    PropType PropObject::* Prop 
    > 
class PropReader 

Vediamo cosa significa. I primi due oggetto argomento template di cui verrà letta la proprietà e PropType, il tipo di tale proprietà. "L'argomento finale del modello è un membro puntatore alla classe denominato Prop che punta all'interno di uno PropObject in un campo di digitare PropType Ad esempio, è possibile creare un'istanza per questo modello con MyStruct in questo modo:.

PropReader<MyStruct, int, &MyStruct::x> myPropReader; 

Ora, vediamo quello che il resto del codice fa il corpo di questo modello di classe è ristampato qui:.

void print(Object& o) 
{ 
    PropObject& po = static_cast<PropObject &>(o); 
    PropType& t = po.*Prop; 

    cout << t << "\n"; 
} 

Alcuni di questi possono essere letti abbastanza facilmente. a questa funzione si riferisce un Object denominato o e l'ultima riga stampa un campo. Queste due linee sono difficili:

PropObject& po = static_cast<PropObject &>(o); 
PropType& t = po.*Prop; 

Questa prima riga è un typecast che dice "cerca di lanciare l'argomento o ad un riferimento di tipo PropObject L'idea, sto cercando di indovinare, è che Object è una certa classe di base. di molti oggetti diversi.Il parametro della funzione è semplicemente un Object, e questo cast tenta di convertirlo in qualcosa del tipo appropriato (ricorda che PropObject è l'argomento del modello che dice qual è il tipo di oggetto). questo utilizza static_cast, se la conversione non è definita (ad esempio, si è tentato di creare un'istanza del modello su int o vector<string>), il codice non verrà compilato. e, il codice si fida che il cast sia sicuro, quindi ottiene un riferimento di tipo PropObject a cui si riferisce il parametro.

Infine, l'ultima riga è

PropType& t = po.*Prop; 

Questo utilizza la sintassi dereference puntatore-a-class-membro ho detto prima a dire "selezionare il campo puntato dal Prop (l'argomento template), poi negozio un riferimento ad esso chiamato t.

Così, in breve, il modello

  1. ti chiede il tipo di un oggetto.
  2. Richiede il tipo di campo in quell'oggetto.
  3. Richiede un puntatore al campo in quell'oggetto.
  4. Fornisce una funzione print che a un oggetto tenta di stampare quel campo.

Whew! E 'stato difficile! Spero che questo ti aiuti!

+1

Questa è stata una risposta sorprendente templatetypedef .. non mi sorprende che tu abbia quel nome utente :) – cgcoder

3

PropObject::* è un puntatore a membro (membro dati, in questo caso). È fondamentalmente un puntatore a un membro (non statico) (che sono Student::age e Student::grade nel caso del codice).

Per utilizzarlo, è necessario specificare l'oggetto this che la funzione utilizzerà. È possibile utilizzare l'operatore .* o ->*; in questo caso, la linea PropType& t = po.*Prop; gestisce ciò, dove po viene utilizzato come oggetto this per i membri.

1

Questa è la sintassi per passare un puntatore a un membro come argomento. Specificamente in questo caso è possibile inoltrare i membri age e grade. Mentre gli argomenti del modello specificano la classe Student di cui è membro e che le proprietà del membro sono int.

Se è stato aggiunto un nome di stringa a studente la dichiarazione PropReader potrebbe assomigliare a questo:

PropReader<Student, std::string, &Student::name> r3; 
0

Ma questo particolare PropType PropObject :: * Prop nel modello di dichiarazione mi dà fastidio. Cosa significa?

PropType PropObject::* Prop 

Definisce puntatore che indica una variabile membro di una classe, in questo caso particolare, la classe è PropObject e il tipo di variabile è PropType.

4

dichiarazioni in C o C++ sono spesso meglio leggere da destra a sinistra:

PropType PropObject::* Prop 
         ~~~~ The Prop template parameter 
        ~~~  is a pointer to a member 
     ~~~~~~~~~~   of a PropObject 
~~~~~~~~      where that member has type PropType 

Questo può essere visto in azione nelle istanze:

PropReader<Student, int, &Student::age> r; 

Ecco il terzo parametro di template è un puntatore a un membro della classe Student, che ha tipo int.

Problemi correlati