Translate

domingo, 24 de noviembre de 2024

¿Por qué C++ utiliza punteros para implementar el polimorfismo?


En C++, los punteros son fundamentales para implementar el polimorfismo debido a cómo se gestionan los objetos y sus referencias en tiempo de ejecución. 

En C++, el polimorfismo en tiempo de ejecución se logra mediante el uso de funciones virtuales y tablas de métodos virtuales (vtable). Para que el compilador pueda llamar al método adecuado de una clase derivada, necesita acceso indirecto al objeto en memoria.

  • Cuando usamos punteros o referencias, el compilador puede determinar el tipo del objeto en tiempo de ejecución y acceder a la vtable correcta.
  • Si usáramos valores en lugar de punteros, el objeto sería copiado, y la información dinámica sobre su tipo (necesaria para el polimorfismo) se perdería.


#include <iostream>

using namespace std;


class Base {

public:

    virtual void speak() const {

        cout << "Soy Base\n";

    }

};


class Derived : public Base {

public:

    void speak() const override {

        cout << "Soy Derived\n";

    }

};


int main() {

    Base* obj = new Derived(); // Usamos puntero para polimorfismo

    obj->speak();             // Llama al método de Derived en tiempo de ejecución

    delete obj;

}


Aquí, el puntero obj permite que la llamada a speak() sea resuelta dinámicamente en tiempo de ejecución.

Otro problema es el slicing, que ocurre cuando un objeto derivado se asigna a un objeto base por valor. Esto copia solo la parte de la clase base, perdiendo los datos específicos de la clase derivada.

Por ejemplo: 


Derived d;

Base b = d; // Copia solo la parte "Base" del objeto

b.speak();  // Siempre llamará a Base::speak(), incluso si es virtual


El uso de punteros evita este problema, ya que siempre apuntan al objeto completo en memoria.

En C++, el polimorfismo suele estar vinculado a la asignación dinámica. Los punteros son necesarios para gestionar la memoria del heap y permiten que un programa decida en tiempo de ejecución qué tipo de objeto crear y manipular.


Base* createObject(bool flag) {

    if (flag)

        return new Derived();

    else

        return new Base();

}


Sin punteros, esta flexibilidad sería difícil de implementar.

Por ultimo, los punteros permiten acceder a objetos grandes en el heap sin necesidad de copiar toda su estructura. Esto es especialmente útil para jerarquías complejas de clases.

En vez de manejar múltiples copias de objetos derivados, los punteros permiten trabajar con una referencia común al objeto base, pero respetando el comportamiento dinámico. Si bien esto no tiene que ver directamente con el polimorfismo lo agregamos para ser más completos :D


C++ utiliza punteros para implementar el polimorfismo porque permiten:

  • Resolución dinámica de métodos a través de las vtables.
  • Evitar el problema de slicing.
  • Compatibilidad con asignación dinámica.
  • Referencias eficientes a objetos complejos.