programacion polimorfismo orientada objetos metodos herencia constructores composicion codigo clases c++ language-design

orientada - polimorfismo c++



¿Por qué no hay una clase base en C++? (6)

C ++ es un lenguaje fuertemente tipado. Sin embargo, es desconcertante que no tenga un tipo de objeto universal en el contexto de la especialización de plantillas.

Tomemos, por ejemplo, el patrón

template <class T> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...)> { ... ReturnType operator () (ArgTypes... args) { ... } };

que se puede instanciar como

Hook<decltype(some_function)> ...;

Ahora supongamos que queremos lo mismo para una función en particular. Me gusta

template <auto fallback> class Hook; template <auto fallback, class ReturnType, class ... ArgTypes> class Hook<ReturnType fallback(ArgTypes...)> { ... ReturnType operator () (ArgTypes... args) { ... } };

con la instanciación especializada

Hook<some_function> ...

Pero, por desgracia, aunque la clase T puede representar cualquier tipo (de clase o no) antes de la especialización, no hay un auto fallback equivalente (estoy usando esa sintaxis como el tipo genérico más obvio en este contexto) que podría figurar en para cualquier argumento de plantilla sin tipo antes de la especialización.

Por lo tanto, en general, este patrón no se transfiere de los argumentos de plantilla tipo a los argumentos de plantilla no tipo.

Al igual que con muchas esquinas en el lenguaje C ++, la respuesta es probable: "ningún miembro del comité pensó en ello".

Pregunta rápida: desde un punto de vista de diseño, ¿por qué es que, en C ++, no hay una clase base de madre de familia, lo que normalmente es object en otros idiomas?


C ++ inicialmente se llamaba "C con clases". Es una progresión del lenguaje C, a diferencia de algunas otras cosas más modernas como C #. Y no puede ver a C ++ como un lenguaje, sino como una base de idiomas (Sí, estoy recordando el libro de Scott Meyers Effective C ++).

C en sí es una mezcla de idiomas, el lenguaje de programación C y su preprocesador.

C ++ agrega otra mezcla:

  • el enfoque clase / objetos

  • plantillas

  • el STL

Personalmente, no me gustan algunas cosas que vienen directamente de C a C ++. Un ejemplo es la función enum. La forma en que C # permite que el desarrollador la use es mucho mejor: limita la enumeración en su propio alcance, tiene una propiedad Count y es fácilmente iterable.

Como C ++ quería ser retrocompatible con C, el diseñador fue muy permisivo al permitir que el lenguaje C ingresara en su totalidad a C ++ (hay algunas diferencias sutiles, pero no recuerdo nada que pudieras hacer usando un compilador de C que no podría hacer usando un compilador de C ++).


El paradigma dominante para las variables de C ++ es pass-by-value, no pass-by-ref. Obligar a que todo se derive de un Object raíz haría pasar por valor un error ipse facto.

(Debido a que aceptar un Objeto por valor como parámetro, por definición lo cortaría y eliminaría su alma).

Esto no es bienvenido C ++ te hace pensar si querías valor o semántica de referencia, dándote la opción. Esto es una gran cosa en la informática de rendimiento.


La decisión definitiva se encuentra en las preguntas frecuentes de Stroustrup . En resumen, no transmite ningún significado semántico. Tendrá un costo. Las plantillas son más útiles para los contenedores.

¿Por qué C ++ no tiene un objeto de clase universal?

  • No necesitamos uno: la programación genérica proporciona alternativas seguras de tipo estático en la mayoría de los casos. Otros casos se manejan usando herencia múltiple.

  • No existe una clase universal útil: un verdadero universal no tiene semántica propia.

  • Una clase "universal" fomenta el pensamiento descuidado sobre tipos e interfaces y conduce a un exceso de comprobación en tiempo de ejecución.

  • El uso de una clase base universal implica un costo: los objetos deben asignarse en forma de pila para que sean polimórficos; eso implica la memoria y el costo de acceso. Los objetos Heap no son compatibles con la semántica de copia. Los objetos Heap no son compatibles con un comportamiento de ámbito simple (lo que complica el manejo de los recursos). Una clase base universal fomenta el uso de dynamic_cast y otras comprobaciones en tiempo de ejecución.


Primero, pensemos en por qué querría tener una clase base en primer lugar. Puedo pensar en algunas razones diferentes:

  1. Para admitir operaciones o colecciones genéricas que funcionarán en objetos de cualquier tipo.
  2. Para incluir varios procedimientos que son comunes a todos los objetos (como la gestión de memoria).
  3. Todo es un objeto (¡no primitivos!). Algunos lenguajes (como Objective-C) no tienen esto, lo que hace que las cosas sean bastante complicadas.

Estas son las dos buenas razones por las que los idiomas de las marcas Smalltalk, Ruby y Objective-C tienen clases base (técnicamente, Objective-C realmente no tiene una clase base, pero para todos los efectos, sí lo tiene).

Para el n. ° 1, la necesidad de una clase base que unifique todos los objetos bajo una sola interfaz se obvia con la inclusión de plantillas en C ++. Por ejemplo:

void somethingGeneric(Base); Derived object; somethingGeneric(object);

es innecesario, cuando puedes mantener la integridad del tipo a través de un polimorfismo paramétrico.

template <class T> void somethingGeneric(T); Derived object; somethingGeneric(object);

Para # 2, mientras que en Objective-C, los procedimientos de administración de memoria son parte de la implementación de una clase, y se heredan de la clase base, la administración de memoria en C ++ se realiza usando composición en lugar de herencia. Por ejemplo, puede definir un contenedor de puntero inteligente que realizará el recuento de referencias en objetos de cualquier tipo:

template <class T> struct refcounted { refcounted(T* object) : _object(object), _count(0) {} T* operator->() { return _object; } operator T*() { return _object; } void retain() { ++_count; } void release() { if (--_count == 0) { delete _object; } } private: T* _object; int _count; };

Entonces, en lugar de llamar a métodos en el objeto en sí, estarías llamando a métodos en su envoltorio. Esto no solo permite una programación más genérica: también le permite separar las preocupaciones (ya que, idealmente, su objeto debería estar más preocupado por lo que debería hacer, que por la forma en que su memoria debe administrarse en diferentes situaciones).

Por último, en un lenguaje que tiene primitivos y objetos reales como C ++, se pierden los beneficios de tener una clase base (una interfaz consistente para cada valor), ya que entonces tiene ciertos valores que no pueden ajustarse a esa interfaz. Para usar primitivas en ese tipo de situación, debe elevarlas a objetos (si su compilador no lo hará automáticamente). Esto crea muchas complicaciones.

Entonces, la respuesta corta a su pregunta: C ++ no tiene una clase base porque, al tener un polimorfismo paramétrico mediante plantillas, no es necesario.


¡El problema es que HAY tal tipo en C ++! Es void :-) Cualquier puntero puede ser implícitamente lanzado a void * , incluyendo punteros a tipos básicos, clases sin tabla virtual y clases con tabla virtual.

Como debe ser compatible con todas esas categorías de objetos, el void sí mismo no puede contener métodos virtuales. Sin funciones virtuales y RTTI, no se puede obtener información útil sobre el tipo de void (coincide con CADA tipo, por lo que solo puede decir cosas que son verdaderas para CADA tipo), pero las funciones virtuales y RTTI hacen que los tipos simples sean muy ineficaces y detienen C ++ siendo un lenguaje adecuado para programación de bajo nivel con acceso directo a memoria, etc.

Entonces, hay tal tipo. Simplemente proporciona una interfaz muy minimalista (de hecho, vacía) debido a la naturaleza de bajo nivel del lenguaje. :-)