Saltar a contenido

Última actualización:2026-10-13

Funciones virtuales en C++

Sobre el autor:

Cuenta pública de WeChat personal:

Funciones virtuales en C++ | C++那些事

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

Código correspondiente: emp.cpp

La invocación de 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 vptr y vtable

3. Argumentos por defecto en funciones virtuales

Código correspondiente: default_arg.cpp

Los argumentos por defecto se enlazan estáticamente, mientras que las funciones virtuales se enlazan dinámicamente. El argumento por defecto utilizado depende del tipo del propio puntero o referencia, no del tipo del objeto.

4. ¿Está permitido?

(1) ¿Puede una función estática declararse 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

Una función miembro static no pertenece a ningún objeto de la clase ni a ninguna instancia de la clase, por lo que añadirle virtual no tiene ningún sentido.

Las funciones virtuales se apoyan en el vptr y la 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 virtual?

Un constructor no puede declararse virtual. Además, aparte de inline y explicit, un constructor no admite ninguna otra palabra clave.

¿Por qué un constructor no puede ser virtual?

Aunque la tabla de funciones virtuales (vtable) se construye en la fase de compilación, el puntero a la tabla de funciones virtuales (vptr) solo se genera en tiempo de ejecución, cuando se instancia el objeto. Si una 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 a la vtable, pero en ese momento el vptr aún no existe. Por tanto, un constructor no puede ser virtual.

Usamos funciones virtuales porque necesitamos un despacho polimórfico con información incompleta. Un constructor, en cambio, sirve para inicializar una instancia, y el tipo de la instancia debe estar claro. Por ello no hay ninguna necesidad de declarar virtual un constructor.

Código para estudiar:

(3) ¿Puede un destructor ser virtual?

Un destructor puede declararse virtual. Si necesitamos eliminar mediante un puntero a la clase base un objeto de una clase derivada, el destructor debe declararse virtual. De hecho, siempre que una clase pueda ser heredada por otras clases, debería declararse un destructor virtual (aunque ese destructor no haga nada).

Código para estudiar:

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

  • Cuando 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 informa: ptr no puede acceder a la función privada. Por supuesto, si la clase base se declara public y la derivada private, el problema desaparece.

Código para estudiar:

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

Normalmente el compilador considera si integrar (inline) las funciones miembro de una clase. Pero una función virtual invocada mediante un puntero o referencia a la clase base jamás puede integrarse. Por supuesto, cuando se llama a una función virtual sobre un objeto real o se la invoca estáticamente, sí puede integrarse, y la invocación estática de un destructor virtual siempre se expandirá en línea.

  • 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 integrarse.
  • La integración en línea es una sugerencia al compilador en tiempo de compilación, mientras que el polimorfismo de las funciones virtuales ocurre en tiempo de ejecución; el compilador no puede saber qué código se invocará en ejecución, por lo que una función virtual no puede integrarse cuando se comporta polimórficamente (en tiempo de ejecución).
  • El único caso en que inline virtual puede integrarse es cuando el compilador sabe a qué clase pertenece el objeto invocado (por ejemplo Base::who()), lo cual solo sucede cuando el compilador dispone del objeto real y no de un puntero o referencia al objeto.

Código para estudiar:

5. RTTI y dynamic_cast

RTTI (Run-Time Type Identification, identificación de tipos en tiempo de ejecución) permite que un programa, mediante un puntero o una referencia a una clase base, compruebe el tipo real de la clase derivada del objeto al que apunta ese puntero o referencia.

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 similar al instanceof de Java y a los operadores as e is de C#, C++ ofrece dynamic_cast para la conversión dinámica. Frente a las conversiones forzadas al estilo de C y a reinterpret_cast de C++, dynamic_cast proporciona comprobaciones de seguridad de tipos y es una conversión basada en consultas de capacidad (Capability Query), por lo que se recomienda usar dynamic_cast para conversiones entre tipos polimórficos.

Código para estudiar:

6. Funciones virtuales puras y clases abstractas

Ver Funciones virtuales puras y clases abstractas

Temas relacionados

Comentarios