2010-11-26 23 views
6

Estoy intentando implementar algún mecanismo en C++ mediante el cual a todas las clases derivadas de una clase base común se les asigna una "ID de clase" única. Por ejemplo:TypeID para las clases derivadas de una clase base común

class BaseClass 
{ 
    //... 
    public: unsigned int GetID(void); 
    //... 
}; 
class DerivedClass : public BaseClass 
{ 
} 

Clase DerivedClass, y todos los demás hijos de BaseClass, debe ser capaz de volver identificadores únicos sin ningún código adicional añadido a DerivedClass ... C++ está haciendo esto bastante difícil para mí, sin embargo. Cualquier idea sería apreciada.

¡Gracias de antemano! --- Dan

+0

si el ID es int o puede ser de un tipo único, p. Ej. el devuelto por typeid. ¿Tu BaseClass es polimórfico? – Chubsdad

+0

Mi clase base ES polimórfica, pero debido a mi falta de capacidad para encontrar una explicación sólida y exhaustiva sobre la naturaleza exacta de la sobrecarga que acompaña a RTTI (tanto en cuanto a rendimiento como a memoria), intento evitarlo por completo. . – Dan

Respuesta

2

No indica que está familiarizado con typeid y dynamic_cast.

Es probable que resuelvan su problema.

Si no es así, por favor describa el motivo por el que no.

Saludos & HTH.,

+0

¿No es eso un comentario? – Chubsdad

+0

Disculpa, se olvidó de mencionar: Tratar de evitar RTTI si es posible, ya que he escuchado diversas historias sobre cuánto sobrecarga implica y no quiero correr riesgos, ya que el rendimiento es fundamental para mi aplicación. – Dan

+0

@Chubstad: no, no es un comentario. 'typeid' es la respuesta general cuando quieres un tipo de identificación. incluso puedes inferir eso del nombre. –

1

Como dice Alf, esto no debería ser necesario. typeid ya proporciona un identificador de clase único, aunque el identificador no es un número entero. Apenas para las risas, si se me permite relajar la condición de "clase base común", entonces:

inline unsigned int counter() { 
    static unsigned int count = 0; 
    return ++count; 
} 

struct BaseClass { 
    virtual unsigned int GetID() = 0; 
    virtual ~BaseClass() {} 
}; 

template <typename D> 
struct IntermediateClass : BaseClass { 
    virtual unsigned int GetID() { 
     static unsigned int thisid = counter(); 
     return thisid; 
    } 
}; 

// usage 
struct Derived : IntermediateClass<Derived> { 
    ... 
}; 

Se necesitaría añadir hilo de seguridad en counter si es para ser utilizado en programas multihilo.

Obviamente, la identificación es única dentro de una ejecución determinada del programa.

se pone un poco difícil si su jerarquía de herencia es profundo, y si usted tiene un montón de constructores con diferentes firmas para diferentes clases, ya que es necesario para insertar IntermediateClass entre cada clase derivada y su clase base directa. Pero siempre se puede rescatar de todo lo que la siguiente manera:

inline unsigned int counter() { 
    static unsigned int count = 0; 
    return ++count; 
} 

struct BaseClass { 
    virtual unsigned int GetID() = 0; 
    virtual ~BaseClass() {} 
}; 

template <typename D> 
unsigned int ThisID(const D *) { 
    static unsigned int thisid = counter(); 
    return thisid; 
} 

// usage 
struct Derived : BaseClass { 
    // this single line pasted in each derived class 
    virtual unsigned int GetID() { return ThisID(this); } 
    ... 
}; 

Creo que hay una "oportunidad" para una nueva característica del lenguaje aquí: una función virtual que se define en la clase base como una función de plantilla con uno " typename "template parameter, y que se reemplaza automáticamente en cada clase derivada utilizando esa clase derivada como argumento de la plantilla. sintaxis imaginaria, ya que las funciones de plantilla virtuales son ilegales:

struct BaseClass { 
    template <typename Derived> 
    virtual unsigned int GetID() { 
     static unsigned int thisid = counter(); 
     return thisid; 
    } 
    virtual ~BaseClass() {} 
}; 

difícil de justificar una característica del lenguaje sobre la base de querer volver a implementar RTTI nosotros mismos, la mente ...

+0

Esto funciona bien, pero pierdo el polimorfismo de mi clase ya que las dos clases derivadas ahora heredan de dos clases diferentes. – Dan

+0

@Dan: He estado planteando un poco desde que dijiste que tus clases eran polimórficas. En la primera opción, todo se deriva indirectamente de BaseClass, por lo que aún puede usar Objetos derivados a través de BaseClass *. En la segunda opción, todo se deriva directamente de BaseClass, lo que hace que sea mucho más fácil administrar los constructores, pero debe agregar una función (idéntica) a cada clase derivada. –

+0

Ratas, acabo de darme cuenta de que la línea en cada clase derivada no es idéntica; tiene que mencionar la clase para eliminar la ambigüedad de la llamada en el caso en que una de estas clases derivadas amplíe otra y, por lo tanto, hereda dos funciones GetIDImpl. Cambiaré esa opción. –

0

Esto requiere modificación de las clases derivadas , pero si la falta de confianza en RTTI es la única razón para evitar la ruta bien establecida de typeid, dynamic_cast, THEN

Algo así debería ser una buena apuesta. También devuelve un 'int' en comparación con 'type_info' por RTTI.

class BaseClass{ 
    //... 
    public: 
     virtual unsigned int GetID(void); 
    //... 
}; 

class DerivedClass : public BaseClass{ 
public: 
    virtual unsigned int GetID(void); 
}; 
+0

Estoy tratando de evitar una implementación separada para cada DerivedClass; ese es mi problema. Lo siento si no lo dejé claro. – Dan

2

Usted debe escuchar a Alf :) Aquí está mi análisis: en el mundo puro, la identificación de las implementaciones amenaza polimorfismo función virtual, no se puede exigir y no debe ser necesario.

En el sucio mundo de la programación real, usted puede tener algunas razones para la identificación única, tales como el cálculo de referencias de datos en el disco, la identificación de los mensajes de diagnóstico, el seguimiento del flujo de control, acumulando estadísticas de uso, etc.

Si sus pensamientos son puros , tu diseño está mal. Vete y descubre por qué no puedes requerir una identificación única.

Si sus pensamientos están corrompidos por la realidad, entonces ya está dispuesto a pagar un precio en rendimiento y memoria para cumplir con sus requisitos, y luego por especificación el costo de usar las características integradas del lenguaje vale la pena pagar, ya que es prácticamente la única manera de lograr su objetivo de proporcionar un servicio de identificación no invasivo. Por no invasivo quiero decir que no necesita agregar nada a cada clase derivada. Es evidente que se debe agregar algo, por lo que si no está dispuesto a hacerlo, no tiene más remedio que aceptar lo que el compilador agrega para usted.

El problema principal aquí es que si está utilizando bibliotecas compartidas cargadas dinámicamente (DLLS), RTTI puede no funcionar como se espera. Esto no solo afecta al tipo de letra negativamente, sino que también puede evitar que se capturen las excepciones que espera quedar atrapado (¡me han picado!). Es posible que se necesite cierto cuidado para garantizar que los vtables y otros RTTI se creen de manera única. Esto puede significar, por ejemplo, que si los vtables se enganchan en su destructor, no está en línea, porque puede generarse en más de un lugar en ese caso, destruyendo la singularidad. Es posible que sea necesario realizar algunas operaciones de pirateo en ausencia de la compatibilidad con la estandarización ISO para la carga dinámica.

+0

Parece que sabe bastante sobre el funcionamiento interno de RTTI. ¿Hay algún sitio en línea que conozcas que tenga una descripción detallada de los mecanismos utilizados a nivel de compilador para implementar la función? – Dan

+0

No tengo enlaces a la mano, pero los lugares para buscar son, por supuesto, los sitios de desarrollo de gcc, y las especificaciones ABI de la industria, originalmente producidas por AMD pero ahora administradas por un grupo de la industria. Básicamente, establece la API utilizada para los procesadores x86_64 para varias funciones de C++, incluido el paso de argumentos en registros, manejo de excepciones, enlaces dinámicos, etc. – Yttrill

Cuestiones relacionadas