Saltar a contenido

Última actualización:2026-10-12

Funciones virtuales (virtual) en C++

Sobre el autor:

Cuenta pública personal:

wechat

1. Funciones virtuales y polimorfismo en tiempo de ejecución

Código correspondiente: emp.cpp

La llamada a una función virtual depende del tipo del objeto apuntado o referenciado, no del tipo del propio puntero o referencia.

2. vptr y vtable

Ver temas sobre vptr_vtable

3. Parámetros por defecto en funciones virtuales

Código correspondiente: default_arg.cpp

Los parámetros por defecto se enlazan estáticamente, mientras que las funciones virtuales se enlazan dinámicamente. El uso de un parámetro por defecto depende del tipo del propio puntero o referencia, no del tipo del objeto.

4. Qué se puede y qué no se puede

(1) ¿Puede una función estática declararse como virtual?

Hay dos razones principales:

Una función estática no puede declararse virtual, ni tampoco puede modificarse con las palabras clave const y volatile

La función miembro static no pertenece a ningún objeto de clase ni instancia de clase, de modo que añadirle virtual no tendría ningún sentido

Las funciones virtuales se apoyan en el vptr y el vtable. El vptr es un puntero que se crea en el constructor de la clase y solo puede accederse a él mediante el puntero this; las funciones miembro estáticas no tienen puntero this, por lo que no pueden acceder al vptr.

Código para estudiar: static_error.cpp

(2) ¿Puede un constructor ser una función virtual?

Un constructor no puede declararse virtual. Además, salvo inline|explicit, el constructor no admite ninguna otra palabra clave.

¿Por qué el constructor no puede ser una función virtual?

Aunque la tabla de funciones virtuales vtable se construye ya en la fase de compilación, el puntero a la tabla de funciones virtuales, vptr, no se genera hasta la fase de ejecución, al instanciar el objeto. Si la clase contiene funciones virtuales, el compilador añade código en el constructor para crear el vptr. Ahí está el problema: si el constructor fuera virtual, necesitaría el vptr para acceder al vtable, pero en ese momento el vptr aún no se ha generado. Por eso, el constructor no puede ser una función virtual.

Usamos funciones virtuales porque necesitamos ejecución polimórfica con información incompleta. El constructor, en cambio, sirve para inicializar la instancia, y el tipo de la instancia debe ser inequívoco. Por lo tanto, no hay ninguna necesidad de declarar el constructor como función virtual.

Código para estudiar:

(3) ¿Puede el destructor ser una función virtual?

El destructor puede declararse virtual. Si necesitamos eliminar mediante un puntero a la clase base un objeto de la clase derivada, el destructor debe declararse virtual. De hecho, siempre que una clase pueda ser heredada por otras, debería declararse un destructor virtual (aunque ese destructor no ejecute ninguna operación).

Código para estudiar:

(4) ¿Puede una función virtual ser privada?

  • Si un puntero a la clase base apunta a un objeto de la clase derivada, se invoca la función del objeto de la clase derivada;
  • int main() debe declararse friend de la clase Base, de lo contrario la compilación falla. El compilador da el error: ptr no puede acceder a la función privada. Por supuesto, si la clase base se declara public y la clase derivada private, el problema desaparece.

Código para estudiar:

(5) ¿Puede una función virtual ser inline?

Normalmente el compilador considera si conviene insertar (inline) las funciones miembro de una clase. Pero una función virtual invocada mediante puntero o referencia a la clase base no puede ser inline en ningún caso. Por supuesto, cuando la invocación se hace desde el objeto real, o cuando la llamada es estática, sí puede insertarse, y la llamada estática a un destructor virtual también se expandirá inline.

  • Una función virtual puede ser una función inline; inline puede modificar a una función virtual, pero cuando la función virtual manifiesta polimorfismo no puede insertarse.
  • inline es una sugerencia al compilador para que inserte en tiempo de compilación, mientras que el polimorfismo de la función virtual ocurre en tiempo de ejecución; el compilador no puede saber qué código se invocará en ejecución, por eso una función virtual no puede ser inline cuando manifiesta polimorfismo (en tiempo de ejecución).
  • inline virtual solo puede insertarse cuando el compilador sabe de qué clase es el objeto invocado (por ejemplo Base::who()), lo cual solo ocurre cuando el compilador tiene el objeto real y no un puntero o referencia al objeto.

Código para estudiar:

5. RTTI y dynamic_cast

RTTI (Run-Time Type Identification): gracias a la información de tipos en tiempo de ejecución, un programa puede usar un puntero o una referencia a la clase base para comprobar el tipo real de la clase derivada del objeto al que apuntan o refieren.

En la programación orientada a objetos, a veces necesitamos consultar en tiempo de ejecución si un objeto puede usarse como un determinado tipo polimórfico. De forma análoga al instanceof de Java y a los operadores as e is de C#, C++ ofrece la función dynamic_cast para la conversión dinámica. Frente a la conversión forzada al estilo de C y al reinterpret_cast de C++, dynamic_cast proporciona comprobación de seguridad de tipos: es una conversión basada en consulta de capacidades (Capability Query), por lo que para convertir entre tipos polimórficos es más recomendable usar dynamic_cast.

Código para estudiar:

6. Funciones virtuales puras y clases abstractas

Ver temas sobre funciones virtuales puras y clases abstractas

Temas relacionados

Comentarios