resueltos polimorfismo metodos herencia ejercicios ejemplos constructores codigo clases c++ inheritance private

polimorfismo - Acceso de funciones miembro en herencia C++.



metodos en c++ (7)

¿Por qué D :: f () se puede invocar aunque sea privado?

Para comprender el mecanismo de la función virtual es bueno saber cómo se implementa normalmente. Una función en tiempo de ejecución no es más que una dirección en la memoria donde se ubica el código ejecutable del cuerpo de la función. Para llamar a la función necesitamos saber su dirección (un puntero). El objeto C ++ con representación de funciones virtuales en la memoria contiene lo que se llama vtable , una matriz de punteros a las funciones virtuales.

El punto clave es que en las clases derivadas vtable repite (y puede extender) la vtable de la clase base, pero si la función virtual se anula, su puntero se reemplaza en la vtable del objeto derivado.

Cuando se realiza una llamada a la función virtual a través del puntero de la clase base, la dirección de la función virtual se calcula como un desplazamiento en la matriz vtable. No se realizan otras comprobaciones, solo se toma la dirección de la función. Si es un objeto de clase base, será la dirección de la función de clase base. Si es un objeto de clase derivada, será la dirección de la función de clase derivada, no importa si se declaró privada o no.

Así funciona.

Estoy confundido acerca del pequeño programa sobre herencia a continuación:

#include<iostream> using namespace std; struct B { virtual int f() { return 1; } }; // f is public in B class D : public B { int f() { return 2; } }; // f is private in D int main() { D d; B& b = d; cout<<b.f()<<endl; // OK: B::f() is public, D::f() is invoked even though it''s private cout<<d.f()<<endl; // error: D::f() is private }

  1. No puedo entender por qué D::f() es privado, D es público heredado de B , por lo que la función pública f en B
    también es público en D (lo sé sin herencia, el acceso de los miembros es privado por defecto)
  2. f es una función virtual en B , así que si llamamos bf() , en realidad llamamos D::f() , pero al igual que la ilustración mencionada, ¿por qué D::f() puede invocarse aunque sea privada?

¿Alguien puede explicar el problema de herencia simple en detalle?


El estándar de C ++ tiene un ejemplo exacto de esto:

11.5 Acceso a funciones virtuales [class.access.virt]

1 Las reglas de acceso (Cláusula 11) para una función virtual están determinadas por su declaración y no están afectadas por las reglas para una función que luego la reemplaza. [ Ejemplo:

class B { public: virtual int f(); }; class D : public B { private: int f(); }; void f() { D d; B* pb = &d; D* pd = &d; pb->f(); // OK: B::f() is public, // D::f() is invoked pd->f(); // error: D::f() is private }

- ejemplo final ]

No puedo explicarlo más claro.


El miembro de la struct es predeterminado para ser público, y el miembro de la class es predeterminado para ser privado. Entonces f() en B es público y cuando se deriva a D, porque no se declaró explícitamente que es público, por lo que, de acuerdo con las reglas de derivación, se volvió privado.


En realidad, esto tiene menos que ver con el envío virtual y más con el significado de los especificadores de acceso.

La función en sí no es private ; su nombre es.

En consecuencia, la función no puede ser nombrada fuera del alcance de la clase, por ejemplo, desde main . Sin embargo, aún puede hacerlo a través de un nombre que sea public (es decir, la función virtual de la base que está anulada) o desde un ámbito en el que el nombre de la función sea accesible a pesar del calificador private (por ejemplo, una función miembro de esa clase).

Así es como funciona.


Esto tiene que ver con que el despacho virtual es un concepto de tiempo de ejecución. A la clase B no le importa qué clase la extiende, y no le importa si es privada o pública porque no puede saber.

No puedo entender por qué D :: f () es privado, D es público heredado de B, por lo que la función pública f en B también es pública en D (Sé que sin herencia, el acceso de los miembros es privado por defecto)

D::f() es privado porque lo hiciste privado. Esta regla no se afecta por herencia ni por despacho virtual.

f es una función virtual en B, así que si llamamos bf (), en realidad llamamos D :: f (), pero al igual que la ilustración mencionada, ¿por qué D :: f () puede invocarse aunque sea privada?

Porque en realidad, cuando se invoca a bf() , el compilador no tiene idea de a qué función se llamará realmente. Simplemente llamará a la función f() , y como B::f es virtual, la función llamada se elegirá en tiempo de ejecución . El programa de tiempo de ejecución no tiene información sobre qué función es privada o está protegida. Sólo se conocen funciones.

Si la función se elige en tiempo de ejecución, el compilador no puede saber en el momento de la compilación a qué función se llamará, y el especificador de acceso no se puede conocer. De hecho, el compilador ni siquiera intentará verificar si la función llamada será privada o no. El especificador de acceso podría estar en algún código que el compilador no haya visto todavía.

Como has experimentado, no puedes llamar a D::f directamente. Esto es exactamente lo que hará privado: prohibir el acceso directo del miembro. Sin embargo, puede acceder a él indirectamente a través de un puntero o una referencia. El envío virtual que uses lo hará internamente.


La respuesta dada ilustra lo que se está haciendo, pero ¿por qué querría hacer esto, donde la clase base llama funciones virtuales private ?

Bueno, hay un patrón de diseño denominado patrón de método de plantilla que utiliza esta técnica de tener una clase base que llama funciones virtuales privadas en la clase derivada.

struct B { virtual ~B() {}; int do_some_algorithm() { do_step_1(); do_step_2(); do_step_3(); } private: virtual void do_step_1() {} virtual void do_step_2() {} virtual void do_step_3() {} }; class D : public B { void do_step_1() { // custom implementation } void do_step_2() { // custom implementation } void do_step_3() { // custom implementation } }; int main() { D dInstance; B * pB = &dInstance; pB->do_some_algorithm(); }

Esto nos permite no exponer los pasos personalizados de la clase D a la interfaz public , pero al mismo tiempo le permite a B llamar a estas funciones utilizando una función public .


Los especificadores de acceso se aplican solo al nombre de la función , no son una restricción sobre cómo o cuándo se puede llamar a la función por otros medios. Una función privada podría llamarse fuera de la clase si está disponible por algún otro medio que no sea su nombre (por ejemplo, un puntero de función).

Para una clase declarada con palabra clave de class , el especificador de acceso predeterminado es private . Su código es el mismo que:

// ... class D: public B { private: int f() { return 2; } };

Como puedes ver, f es privado en D No importa cuál sea el especificador de acceso de cualquier función en B con el mismo nombre. Tenga en mente que B::f() y D::f() son dos funciones diferentes.

El efecto de la palabra clave virtual es que si se llama a f() sin un calificador de alcance en una referencia B que se refiere a un objeto D , entonces aunque se resuelva a B::f() , en realidad D::f() es invocado en su lugar.

Este proceso todavía utiliza el especificador de acceso para B::f() : el acceso se verifica en tiempo de compilación; pero podría ser una cuestión de tiempo de ejecución en cuanto a qué función se llama.