tipos programacion polimórficas polimorfismo orientada objetos encapsulamiento biología c++ polymorphism

c++ - programacion - Objetos polimórficos en la pila?



tipos de polimorfismo (7)

En una pregunta anterior cité a Stroustrup sobre por qué una clase común de objetos para todas las clases es problemática en c ++. En esa cita hay una declaración:

El uso de una clase base universal implica un costo: los objetos deben asignarse en forma de pila para que sean polimórficos;

Realmente no lo miré dos veces, y dado que está en la página principal de Bjarnes , supongo que muchos ojos han escaneado esa frase e informado sobre cualquier error.

Sin embargo, un comentarista señaló que probablemente este no sea el caso y, en retrospectiva, no puedo encontrar ninguna buena razón para que esto sea cierto. Un breve caso de prueba arroja el resultado esperado de "VDerived :: f ()".

struct VBase { virtual void f() { cout<<"VBase::f()"<<endl; } }; struct VDerived:VBase { void f() { cout<<"VDerived::f()"<<endl; } }; void test(VBase& obj){ obj.f(); } int main(int argc, char** argv) { VDerived obj; test(obj); return 0; }

Por supuesto, si el argumento formal para la prueba fuera la test(VBase obj) el caso sería totalmente diferente, pero eso no sería un argumento de pila versus de montón, sino más bien semántica de copia.

¿Bjarne está completamente equivocado o me falta algo aquí?


La declaración de Bjarne no es correcta.

Los objetos, es decir, instancias de una clase, se vuelven potencialmente polimórficos al agregar al menos un método virtual a su declaración de clase. Los métodos virtuales agregan un nivel de direccionamiento indirecto, lo que permite redirigir una llamada a la implementación real, que la persona que llama puede desconocer.

Para esto, no importa si la instancia está asignada en pila o en pila, siempre que se acceda a través de una referencia o puntero ( T& instance o T* instance ).

Una posible razón por la que esta afirmación general se deslizó en la página web de Bjarne podría ser que, no obstante, es muy común asignar instancias de almacenamiento dinámico con comportamiento polimórfico. Esto se debe principalmente a que la aplicación real no es conocida por el llamador que la obtuvo a través de una función de fábrica de algún tipo.


Creo que Bjarne quiere decir que obj , o más precisamente, el objeto al que apunta, no puede estar fácilmente basado en la pila en este código:

int f(int arg) { std::unique_ptr<Base> obj; switch (arg) { case 1: obj = std::make_unique<Derived1 >(); break; case 2: obj = std::make_unique<Derived2 >(); break; default: obj = std::make_unique<DerivedDefault>(); break; } return obj->GetValue(); }

No puede tener un objeto en la pila que cambie su clase, o inicialmente no está seguro de a qué clase exacta pertenece.

(Por supuesto, para ser realmente pedante, uno podría asignar el objeto en la pila mediante el uso de la ubicación-nueva en un espacio alloca alloca. alloca embargo, el hecho de que haya soluciones complicadas no viene al caso aquí).

El siguiente código tampoco funciona como podría esperarse:

int f(int arg) { Base obj = DerivedFactory(arg); // copy (return by value) return obj.GetValue(); }

Este código contiene un error de corte de objetos : el espacio de la pila para obj es solo tan grande como una instancia de la clase Base ; cuando DerivedFactory devuelve un objeto de una clase derivada que tiene algunos miembros adicionales, no se copiarán en obj que hace que obj sea ​​inválido e inutilizable como objeto derivado (y muy posiblemente incluso inutilizable como objeto base).

Resumiendo, hay una clase de comportamiento polimórfico que no se puede lograr con objetos apilados de ninguna manera directa.

Por supuesto, cualquier objeto derivado completamente construido, donde quiera que esté almacenado, puede actuar como un objeto base y, por lo tanto, actuar polimórficamente. Esto simplemente se deduce de la relación is-a que los objetos de las clases heredadas tienen con su clase base.


Creo que el punto es que esto no es "realmente" polimórfico (lo que sea que eso signifique :-).

Podría escribir su función de prueba de esta manera

template<class T> void test(T& obj) { obj.f(); }

y todavía funcionaría, si las clases tienen funciones virtuales o no.


Creo que iba por el camino de no poder almacenarlo en una variable de tipo base. Tiene razón al decir que puede almacenarlo en la pila si es del tipo derivado porque no tiene nada de especial; conceptualmente, solo almacena los datos de la clase y sus derivadas + a vtable.

editar: Bien, ahora estoy confundido, volviendo a ver el ejemplo. Parece que puedes estar ahora ...


Después de leerlo creo que el punto es que la clase base universal es inútil para los objetos manejados por valor, por lo que conduciría naturalmente a un mayor manejo por referencia y, por lo tanto, a una mayor sobrecarga de asignación de memoria. vector vs. vector de punteros).

Así que creo que quiso decir que los objetos tendrían que asignarse por separado de cualquier estructura que los contenga y que habría llevado a muchas más asignaciones en el montón. Como está escrito, la declaración es de hecho falsa.

PD (comentario del Capitán Jirafa): Sería inútil tener una función

f(object o)

lo que significa que la función genérica tendría que ser

f(object &o)

Y eso significaría que el objeto tendría que ser polimórfico, lo que a su vez significa que tendría que asignarse por separado, lo que a menudo significaría en montón, aunque puede estar en la pila. Por otro lado ahora tienes:

template <typename T> f(T o) // see, no reference

que termina siendo más eficiente para la mayoría de los casos. Este es especialmente el caso de las colecciones, donde si todo lo que tenía era un vector de dichos objetos base (como lo hace Java), tendría que asignar todos los objetos por separado. Lo cual sería una gran sobrecarga, especialmente dado el bajo rendimiento del asignador en el momento en que se creó C ++ (Java todavía tiene ventaja porque copiar el recolector de basura es más eficiente y C ++ no puede usar uno).


Me parece polimorfismo.

El polimorfismo en C ++ funciona cuando tienes indirección ; es decir, un pointer-to-T o una reference-to-T Donde se almacena T es completamente irrelevante.

Bjarne también comete el error de decir "montón asignado" que es técnicamente inexacto.

(Nota: ¡esto no significa que una clase base universal sea "buena"!)


Te estás perdiendo algo. Su VDerived obj VDerived no se comporta polimórficamente, se comporta exactamente como un objeto VDerived .