2011-10-26 16 views
32

Mientras investigo el código fuente de Qt, vi que los chicos de Trolltech usan explícitamente la palabra clave this para acceder a un campo en destructor.En una clase derivada de plantillas, ¿por qué debo calificar los nombres de los miembros de la clase base con "this->" dentro de una función miembro?

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

Entonces, ¿de qué sirve este uso? ¿Hay algún beneficio?

Editar: Para aquellos que votan por el cierre de esta pregunta, sospecho que este uso es para algunos casos de herencia de clase

Una parte de QScopedPointer class definición:

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

Respuesta

39

la respuesta C++ (respuesta general)

Considérese una clase de plantilla Derived con una clase base de la plantilla:

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

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

this tiene tipo Derived<T>, un tipo que depende de T. Entonces this tiene un tipo dependiente. Entonces this->d hace d un nombre dependiente. Los nombres dependientes se buscan en el contexto de la definición de la plantilla como nombres no dependientes y en el contexto de creación de instancias.

Sin this->, el nombre d solo se buscará como un nombre no dependiente y no se encontrará.

Otra solución es declarar d en la propia definición de plantilla:

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

Qanswer (respuesta específica)

d es una member of QScopedPointer. No es un miembro heredado. this-> no es necesario aquí.

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> 

por lo this-> es necesario here:

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

Es fácil ver que es más fácil sólo hay que poner this-> todas partes.

entender la razón

supongo que no está claro para todos los usuarios de C++ qué nombres se veía en marcha en las clases base no dependientes, pero no en las clases de base dependientes:

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 
    } 
}; 

Hay una razón al lado de "el estándar lo dice": la causa del enlace del nombre de la vía en las plantillas funciona.

Las plantillas pueden tener un nombre que está vinculado tarde, cuando se crea una instancia de la plantilla: por ejemplo f en f (this). En el punto de definición de Derived2::f(), no hay ninguna variable, función o nombre de tipo f conocido por el compilador.El conjunto de entidades conocidas a las que podría referirse el f está vacío en este momento. Esto no es un problema porque el compilador sabe que buscará f más tarde como un nombre de función, o un nombre de función de plantilla.

OTOH, el compilador no sabe qué hacer con d; no es un nombre de función (llamado). No hay forma de hacer vinculaciones tardías en nombres de funciones no llamadas (llamadas).

Ahora, todo esto puede parecer un conocimiento elemental del polimorfismo de plantilla en tiempo de compilación. La verdadera pregunta parece ser: ¿por qué no está d vinculado a Base<T>::d en el momento de definición de la plantilla?

El problema real es que no hay Base<T>::d en la plantilla de tiempo de definición, porque no hay ningún tipo Base<T> completa en ese momento: Base<T> se declara, pero no definido! Usted puede preguntar: ¿qué pasa con esto:

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

parece que la definición de un tipo completo!

En realidad, hasta ejemplificación, se parece más a:

template <typename T> 
class Base; 

al compilador. ¡No se puede buscar un nombre en una plantilla de clase! Pero solo en una especialización de plantilla (instanciación). La plantilla es una fábrica para hacer especialización de plantillas, , una plantilla no es un conjunto de plantillas de especialización. El compilador puede buscar d en Base<T> para cualquier tipo particular T, pero no puede buscar d en la plantilla de clase Base. Hasta que se determine un tipo T, Base<T>::d sigue siendo el resumen Base<T>::d; solo cuando se conoce el tipo T, Base<T>::d comienza a hacer referencia a una variable del tipo int.

La consecuencia de esto es que la plantilla claseDerived2 tiene una base completa de clase Base0 pero incompleto (avance declarado) Base clase base. Solo para un tipo conocido T, la "clase de plantilla" (especializaciones de una plantilla de clase) Derived2<T> tiene una clase base completa, como cualquier clase normal.

ver ahora que:

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

es en realidad una plantilla base de especificación de la clase (una fábrica de hacer especificaciones de la clase base) que sigue reglas diferentes a partir de una especificación de la clase de base dentro de una plantilla.

Observación: El lector puede haber notado que he inventado algunas frases al final de la explicación.

Esto es muy diferente: aquí d es un nombre calificado en Derived<T> y Derived<T> depende desde T es un parámetro de plantilla. Un nombre calificado puede vincularse tarde incluso si no es un nombre de función (llamado).

Sin embargo, otra solución es:

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

Esto es equivalente.

Si crees que dentro de la definición de Derived<T>, el tratamiento de Derived<T> como una clase completa conocida a veces y como una clase desconocida algunas otras veces en inconsistente, bien, tienes razón.

+0

** ** Nota Moderador _Comments bajo esta respuesta se han purgado ya que degeneraron en puro ruido. Utilice [chat] para discusiones extensas y recuerde permanecer civil._ –

1

yo supongo que se refiere a un uso sobrecargado de la rutina de limpieza(). El tipo que se pasa está explícitamente controlado por el tipo de plantilla T, que a su vez puede controlar qué versión sobrecargada de Cleanup() se invoca.

Cuestiones relacionadas