2012-06-05 30 views
7

¿Cómo puedo imprimir el nombre de clase derivado de la clase base sin tener que encadenar a los constructores hasta el final? En otras palabras, ¿es posible hacer esto estrictamente desde la clase base sin agregar código en cada clase derivada?Impresión del nombre de clase derivada en la clase base

Este es un ejemplo de lo que obtuve, y si hay una manera en la que me gustaría deshacerme del encadenamiento de constructores.

EDIT: Idealmente, estoy buscando algo para agregar a la clase base sin tener que editar todas las clases derivadas. Por el momento, mi código real tiene ~ 17 clases (con la necesidad de más), por lo que algo que podría hacer el trabajo directamente desde la clase base sería ideal. Incluso si es específico del compilador (g ++ o clang).

#include <iostream> 

class Base { 
public: 
    Base(std::string id) { 
      std::cout<<"Creating "<<id<<std::endl; 
    } 
}; 

class Child : Base { 
public: 
    Child(std::string id) : Base(id) {} 
    Child() : Base(typeid(this).name()) {} 
}; 

class GrandChild : Child { 
public: 
    GrandChild(std::string id) : Child(id) {} 
    GrandChild() : Child(typeid(this).name()) {} 
}; 

class GrandGrandChild : GrandChild { 
public: 
    GrandGrandChild(std::string id) : GrandChild(id) {} 
    GrandGrandChild() : GrandChild(typeid(this).name()) {} 
}; 



int main() { 
    GrandGrandChild *A = new GrandGrandChild(); 
    GrandChild *B = new GrandChild(); 
    Child *C = new Child(); 

    return 0; 
} 

que imprime:

Creating GrandGrandChild 
Creating GrandChild 
Creating Child 

Pero con el prefijo añadido compilado.

+1

Usa polimorfismo, no RTTI, para eso sirve el polimorfismo. – Griwes

+0

Tengo la intención de usarlo solo para depuración, que es la razón por la cual algo conveniente incluir en la clase base sería conveniente. – NFA

+2

no crea una instancia de los objetos dinámicamente, p. 'GrandGrandChild A;' sería perfectamente suficiente para evaluar el constructor predeterminado. – moooeeeep

Respuesta

9

Lamentablemente, no hay una solución fácil.

El problema es que la construcción de objetos polimórficos es bastante complicado, en el momento en que se está construyendo el Base subparte de una clase Child, usted está construyendo una Base todavía, no un Child (porque tratar de acceder Child miembros no sería sensical, no se han construido aún!)

Como tal, todas las formas de recuperar información dinámica (conocida como RTTI o información de tipo de tiempo de ejecución) voluntariamente bloquearon para evitar dicho error.

Por razones simétricas, lo mismo ocurre en el destructor.


Ahora, sólo el constructor y el destructor están tan bloqueados, por lo tanto, se puede tener perfectamente un método name() que felizmente devolverá el cierto nombre del tipo dinámico de la instancia en todos los demás casos:

class Base { 
public: 
    std::string name() const { return typeid(*this).name(); } 
}; 

Funcionará ... a menos que lo invoque de un constructor o destructor en cuyo caso informará el tipo estático.

Ahora, en cuanto a la salida "extraña", cada implementación (compilador) puede proporcionar su propia salida aquí (y ni siquiera necesitan ser diferentes para los diferentes tipos, loco, eh!). Parece que estás usando gcc o clang.

Hay demandantes para interpretar dicho resultado, o si su programa es lo suficientemente simple y su interfaz lo asusta, puede simplemente intentar analizarlo manualmente para eliminar el cruft. El nombre de la clase debería aparecer completamente, solo irá precedido de algunas tonterías (espacios de nombres y números en esencia).

+0

Ya veo. Realmente no encontré lo que estaba buscando cuando busqué en Google, así que temía que no se pudiera resolver de la manera que yo lo quisiera. Simplemente creí que la información estaría allí para el compilador, por lo que incluso si está construyendo la parte de la clase base, se habría desplazado desde el nivel superior para poder conservar esa información. – NFA

0

Como indica que esto es para la depuración, puede confiar en la herencia virtual para evitar pasar el nombre a través de todas las clases derivadas intermedias, y en su lugar pasarlo directamente al Base. Además, Base se puede modificar para tomar un constructor de plantilla para simplificar las cosas para las clases derivadas.

class Base { 
public: 
    template <typename DERIVED> 
    Base (DERIVED *d) { 
     std::cout << "Creating " << typeid(*d).name() << std::endl; 
    } 
}; 

class Child : virtual public Base { 
public: 
    Child() : Base(this) {} 
}; 

class GrandChild : public Child, virtual public Base { 
    GrandChild() : Base(this) {} 
} 

class GrandGrandChild : public GrandChild, virtual public Base { 
    GrandGrandChild() : Base(this) {} 
} 
+0

No necesita 'virtual' en la herencia de' Child' y 'GrandChild', solo en la herencia de' Base'. – ymett

+0

@ymett: gracias, edición realizada. – jxh

+0

Por el momento tengo ~ 17 clases (espero que necesite más) que se ramifican desde una clase base. Es por eso que estoy buscando algo que podría agregar fácilmente a la clase base y no editar todas las clases de forma individual. Tal vez lo que estoy preguntando no es posible ... – NFA

0

puede proporcionar una función de inicialización que necesita ser llamada desde cada constructor.

class Base { 
protected: 
    Base() { init(typeid(this).name()); } 
    void init(std::string id) { 
    std::cout<<"Creating "<<id<<std::endl; 
    } 
}; 

De alguna manera tienen que asegurarse de que ensu posterior reemplazará con seguridad los cambios de las anteriores:

Creating P4Base 
Creating P5Child 
Creating P10GrandChild 
Creating P15GrandGrandChild 
Creating P4Base 
Creating P5Child 
Creating P10GrandChild 
Creating P4Base 
Creating P5Child 

que va a utilizarla exclusivamente para fines de depuración, que es por qué algo que arrojar a la clase base sería conveniente.

¿Ha considerado agregar una macro a su código para imprimir la salida de depuración?

#ifdef DEBUG 
    #define PRINT_CLASSNAME std::cout<<"Creating "<<id<<std::endl; 
#else 
    #define PRINT_CLASSNAME ; 
#endif 

es necesario agregar a sus constructores una vez, pero si quieres desactivarlo (temporalmente) que acaba de UNDEFINE ella?

+0

Esto elimina el encadenamiento del constructor pero aún tengo que tener código en cada clase derivada. Estaba buscando alguna solución para simplemente echarme a la clase base. Parece que esta es información que el compilador sabría. Incluso estaría bien con algún código específico del compilador (g ++/clang) si me ayudara. – NFA

+0

@NFA parece que desea agregar algunas funciones de salida de depuración, que desea eliminar tan fácil como sea posible cuando deje de necesitarlo. Por favor, mira mi edición para otra sugerencia. – moooeeeep

+0

@moooeeeep en su camino, aparecerá Creating Base, Creating Child para una sola clase, y al ver el resultado no se puede diferenciar fácilmente de dos clases creadas. –

Cuestiones relacionadas