Pregunta rápida: desde el punto de vista del diseño, ¿por qué, en C ++, no hay una clase base madre de todos, lo que suele ocurrir object
en otros lenguajes?
c++
language-design
slezica
fuente
fuente
typedef
. Con una jerarquía de clases más genérica, podría usarobject
oiterator
.Respuestas:
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.
fuente
Objects must be heap-allocated to be polymorphic
- No creo que esta afirmación sea correcta en general. Definitivamente puede crear una instancia de una clase polimórfica en la pila y pasarla como un puntero a una de sus clases base, lo que produce un comportamiento polimórfico.Foo
en una referencia aBar
cuandoBar
no se heredaFoo
, se lanza de forma determinista una excepción. En C ++, intentar lanzar un puntero a Foo en un puntero a Bar podría enviar un robot atrás en el tiempo para matar a Sarah Connor.Primero pensemos en por qué querrías tener una clase base en primer lugar. Puedo pensar en algunas razones diferentes:
Estas son las dos buenas razones por las que los lenguajes de las marcas Smalltalk, Ruby y Objective-C tienen clases base (técnicamente, Objective-C no tiene realmente una clase base, pero para todos los efectos, la tiene).
Para el n. ° 1, la necesidad de una clase base que unifique todos los objetos bajo una sola interfaz se evita con la inclusión de plantillas en C ++. Por ejemplo:
void somethingGeneric(Base); Derived object; somethingGeneric(object);
es innecesario, cuando se puede mantener la integridad del tipo por completo mediante polimorfismo paramétrico.
template <class T> void somethingGeneric(T); Derived object; somethingGeneric(object);
Para el n. ° 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 utilizando 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; };
Luego, en lugar de llamar a métodos en el objeto en sí, estaría llamando a métodos en su contenedor. Esto no solo permite una programación más genérica: también le permite separar preocupaciones (ya que, idealmente, su objeto debería estar más preocupado por lo que debería hacer que por cómo debería gestionarse su memoria en diferentes situaciones).
Por último, en un lenguaje que tiene tanto primitivas como objetos reales como C ++, se pierden los beneficios de tener una clase base (una interfaz consistente para cada valor), ya que entonces tienes ciertos valores que no pueden ajustarse a esa interfaz. Para usar primitivas en ese tipo de situación, debe convertirlas en objetos (si su compilador no lo hace automáticamente). Esto crea muchas complicaciones.
Entonces, la respuesta corta a su pregunta: C ++ no tiene una clase base porque, al tener polimorfismo paramétrico a través de plantillas, no es necesario.
fuente
object
(System.Object
), pero no es necesario. Para el compilador,int
ySystem.Int32
son alias y pueden usarse indistintamente; el tiempo de ejecución maneja el boxeo cuando es necesario.std::shared_ptr
que debería usarse en su lugar.El paradigma dominante para las variables de C ++ es pasar por valor, no pasar por referencia. Forzar que todo se derive de una raíz
Object
haría que pasarlos por valor fuera un error ipse facto.(Porque aceptar un Objeto por valor como parámetro, por definición lo cortaría y quitarí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 muy importante en la informática de rendimiento.
fuente
Object
sería relativamente pequeño.¡El problema es que existe un tipo de este tipo en C ++! Lo es
void
. :-) Cualquier puntero se puede convertir implícitamente de forma seguravoid *
, incluidos los punteros a tipos básicos, clases sin tabla virtual y clases con tabla virtual.Dado que debería ser compatible con todas esas categorías de objetos, por
void
sí mismo no puede contener métodos virtuales. Sin funciones virtuales y RTTI, no se puede obtener información útil sobre el tipovoid
(coincide con CADA tipo, por lo que solo puede decir cosas que son verdaderas para CADA tipo), pero las funciones virtuales y RTTI harían que los tipos simples fueran muy ineficaces y evitarían que C ++ se un lenguaje adecuado para programación de bajo nivel con acceso directo a memoria, etc.Entonces, existe ese tipo. Simplemente proporciona una interfaz muy minimalista (de hecho, vacía) debido a la naturaleza de bajo nivel del lenguaje. :-)
fuente
void
.void
, no se compilará y obtendrá este error . En Java, podría tener una variable de tipoObject
y funcionaría. Esa es la diferencia entrevoid
un tipo "real". Quizás sea cierto quevoid
es el tipo base para todo, pero como no proporciona ningún constructor, método o campo, no hay forma de saber si está ahí o no. Esta afirmación no se puede probar ni refutar.void
es un tipo (y descubrí un uso? :
algo inteligente de ), pero todavía está muy lejos de ser una clase base universal. Por un lado, es "un tipo incompleto que no se puede completar", y por otro, no hayvoid&
tipo.C ++ es un lenguaje fuertemente tipado. Sin embargo, es sorprendente 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
Pero, por desgracia, aunque la clase T puede representar cualquier tipo (clase o no) antes de la especialización, no hay equivalente
auto fallback
(estoy usando esa sintaxis como el no tipo genérico más obvio en este contexto) que pueda reemplazar a cualquier argumento de plantilla sin tipo antes de la especialización.Entonces, en general, este patrón no se transfiere de argumentos de plantilla de tipo a argumentos de plantilla que no son de tipo.
Al igual que con muchos rincones en el lenguaje C ++, la respuesta probablemente sea "ningún miembro del comité pensó en eso".
fuente
ReturnType fallback(ArgTypes...)
funciona, sería un mal diseño.template <class T, auto fallback> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...), ReturnType(*fallback)(ArgTypes...)> ...
hace lo que quiere y significa que los parámetros de la plantillaHook
son de tiposC ++ se llamó inicialmente "C con clases". Es una progresión del lenguaje C, a diferencia de otras cosas más modernas como C #. Y no se puede ver C ++ como un lenguaje, sino como una base de lenguajes (Sí, me acuerdo del libro de Scott Meyers Effective C ++).
C en sí mismo es una mezcla de lenguajes, el lenguaje de programación C y su preprocesador.
C ++ agrega otra combinación:
el enfoque de clase / objetos
plantillas
el STL
Personalmente, no me gustan algunas cosas que vienen directamente de C a C ++. Un ejemplo es la función de enumeración. La forma en que C # permite que el desarrollador lo 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 ++).
fuente