Diferencia entre herencia privada, pública y protegida

Respuestas:

1065

Para responder a esa pregunta, me gustaría describir los accesos de los miembros primero en mis propias palabras. Si ya sabe esto, salte al encabezado "siguiente:".

Hay tres métodos de acceso que yo sepa: public, protectedy private.

Dejar:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Todo lo que es consciente Basetambién es consciente de que Basecontiene publicMember.
  • Solo los niños (y sus hijos) son conscientes de que Basecontiene protectedMember.
  • Nadie, pero Baseestá al tanto privateMember.

Por "es consciente de", me refiero a "reconocer la existencia de, y así poder acceder".

siguiente:

Lo mismo sucede con la herencia pública, privada y protegida. Consideremos una clase Basey una clase Childque hereda de Base.

  • Si la herencia es public, todo lo que conoce Basey Childtambién sabe que Childhereda de Base.
  • Si la herencia es protected, solo Child, y sus hijos, son conscientes de que heredan de Base.
  • Si la herencia es private, nadie que no Childsea ​​consciente de la herencia.
Anzurio
fuente
183
Me gustaría agregar algunas palabras de que la visibilidad en C ++ se basa en la clase en lugar de en el objeto, lo que significa que los objetos de la misma clase pueden acceder a los campos privados de los demás sin restricciones.
Zhe Chen
48
Si tiene dificultades para entender esto, lea la respuesta de Kirill V. Lyadvinsky, luego regrese y lea esto.
The Vivandiere
66
Este es solo otro caso que ilustra cómo, en su mayor parte, heredar de SomeBasees como una forma codificada de componer un miembro anónimo de tipo SomeBase. Esto, como cualquier otro miembro, tiene un especificador de acceso, que ejerce el mismo control sobre el acceso externo.
underscore_d
1
@ZheChen si tengo objetos Tom y Jerry de clase Persona con edad de campo privado ¿cómo accedes (y modificas) la edad de Jerry usando Tom?
gen
2
¿Podría ilustrar lo que quiere decir con "consciente de la 'herencia'"? Puedo entender "puedo acceder a esto, no puedo acceder a eso" pero no lo entiendo cuando uno dice "Sé que A hereda de B" ¿qué estoy haciendo aquí? ¿Estoy verificando la herencia?
neilxdims
1460
class A 
{
public:
    int x;
protected:
    int y;
private:
    int z;
};

class B : public A
{
    // x is public
    // y is protected
    // z is not accessible from B
};

class C : protected A
{
    // x is protected
    // y is protected
    // z is not accessible from C
};

class D : private A    // 'private' is default for classes
{
    // x is private
    // y is private
    // z is not accessible from D
};

NOTA IMPORTANTE: todas las clases B, C y D contienen las variables x, y y z. Es solo cuestión de acceso.

Sobre el uso de herencia protegida y privada puede leer aquí .

Kirill V. Lyadvinsky
fuente
35
Lo que escribió Anzurio solo hizo clic junto con su respuesta inmediatamente a continuación. Плус 1.
Iwillnotexist Idonotexist
2
¡Comprendí cómo funcionaba esto TAN LEJOS! Muchas gracias por aclarar.
tjwrona1992
Me tomó un tiempo entender esto. Pero ahora está claro. ¡Gracias!
Chan Kim
115

Limitar la visibilidad de la herencia hará que el código no pueda ver que alguna clase hereda otra clase: las conversiones implícitas de lo derivado a la base no funcionarán, y static_castde la base a lo derivado tampoco funcionará.

Solo los miembros / amigos de una clase pueden ver la herencia privada, y solo los miembros / amigos y las clases derivadas pueden ver la herencia protegida.

herencia pública

  1. IS-A herencia. Un botón es una ventana, y en cualquier lugar donde se necesite una ventana, también se puede pasar un botón.

    class button : public window { };

herencia protegida

  1. Protegido implementado en términos de. Raramente útil. Se usa boost::compressed_pairpara derivar de clases vacías y ahorrar memoria usando la optimización de clase base vacía (el ejemplo a continuación no usa la plantilla para mantenerse en el punto):

    struct empty_pair_impl : protected empty_class_1 
    { non_empty_class_2 second; };
    
    struct pair : private empty_pair_impl {
      non_empty_class_2 &second() {
        return this->second;
      }
    
      empty_class_1 &first() {
        return *this; // notice we return *this!
      }
    };

herencia privada

  1. Implementado en términos de. El uso de la clase base es solo para implementar la clase derivada. Útil con rasgos y si el tamaño importa (los rasgos vacíos que solo contienen funciones harán uso de la optimización de clase base vacía). Sin embargo, a menudo la contención es la mejor solución. El tamaño de las cadenas es crítico, por lo que es un uso frecuente aquí

    template<typename StorageModel>
    struct string : private StorageModel {
    public:
      void realloc() {
        // uses inherited function
        StorageModel::realloc();
      }
    };

miembro público

  1. Agregar

    class pair {
    public:
      First first;
      Second second;
    };
  2. Accesorios

    class window {
    public:
        int getWidth() const;
    };

miembro protegido

  1. Proporcionar acceso mejorado para clases derivadas

    class stack {
    protected:
      vector<element> c;
    };
    
    class window {
    protected:
      void registerClass(window_descriptor w);
    };

miembro privado

  1. Mantener detalles de implementación

    class window {
    private:
      int width;
    };

Tenga en cuenta que los lanzamientos de estilo C permiten a propósito lanzar una clase derivada a una clase base protegida o privada de una manera definida y segura y también lanzar en la otra dirección. Esto debe evitarse a toda costa, ya que puede hacer que el código dependa de los detalles de implementación, pero si es necesario, puede utilizar esta técnica.

Johannes Schaub - litb
fuente
77
Creo que Scott Myers (por mucho que me gusten sus cosas) tiene mucho que responder por la confusión general. Ahora creo que sus analogías de IS-A y IS-IMPLEMENTED-IN-TERMS-OF son suficientes para lo que está sucediendo.
DangerMouse
65

Estas tres palabras clave también se utilizan en un contexto completamente diferente para especificar el modelo de herencia de visibilidad .

Esta tabla reúne todas las combinaciones posibles de la declaración de componentes y el modelo de herencia que presenta el acceso resultante a los componentes cuando la subclase está completamente definida.

ingrese la descripción de la imagen aquí

La tabla anterior se interpreta de la siguiente manera (eche un vistazo a la primera fila):

Si un componente se declara como público y su clase se hereda como pública, el acceso resultante es público .

Un ejemplo:

 class Super {
    public:      int p;
    private:     int q;
    protected:   int r;
 };

 class Sub : private Super {};

 class Subsub : public Sub {};

El acceso resultante para las variables p, q, ren la clase subsub es ninguno .

Otro ejemplo:

class Super {
    private:     int x;
    protected:   int y;
    public:      int z;
 };
class Sub : protected Super {};

El acceso resultante para las variables y, zen la clase Sub, está protegido y para la variable xes ninguno .

Un ejemplo más detallado:

class Super {
private:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};
int main(void) {
    Super object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

Ahora definamos una subclase:

class Sub : Super { };

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

La clase definida llamada Sub, que es una subclase de la clase llamada Supero esa Subclase se deriva de la Superclase. La Subclase no introduce nuevas variables ni nuevas funciones. ¿Significa que algún objeto de la Subclase hereda todos los rasgos después de que la Superclase sea, de hecho, una copia de Superlos objetos de una clase?

No se . No lo hace.

Si se compila el código siguiente, obtendremos nada más que errores de compilación diciendo que puty getmétodos son inaccesibles. ¿Por qué?

Cuando omitimos el especificador de visibilidad, el compilador supone que vamos a aplicar la denominada herencia privada . Esto significa que todos los públicos componentes superclase se convierten en privado de acceso, los componentes de la superclase privadas no serán accesibles a todos. En consecuencia, significa que no puede usar este último dentro de la subclase.

Tenemos que informar al compilador que queremos preservar la política de acceso utilizada anteriormente.

class Sub : public Super { };

No se deje engañar : no significa que los componentes privados de la clase Super (como la variable de almacenamiento) se conviertan en públicos de una manera algo mágica. Los componentes privados seguirán siendo privados , los públicos seguirán siendo públicos .

Los objetos de la Subclase pueden hacer "casi" las mismas cosas que sus hermanos mayores creados a partir de la Superclase. "Casi" porque el hecho de ser una subclase también significa que la clase perdió acceso a los componentes privados de la superclase . No podemos escribir una función miembro de la Subclase que pueda manipular directamente la variable de almacenamiento.

Esta es una restricción muy seria. ¿Hay algún trabajo alrededor?

.

El tercer nivel de acceso se llama protegido . La palabra clave protegida significa que el componente marcado con él se comporta como uno público cuando lo utiliza cualquiera de las subclases y parece privado para el resto del mundo . - Esto es cierto solo para las clases heredadas públicamente (como la clase Super en nuestro ejemplo) -

class Super {
protected:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};

class Sub : public Super {
public:
    void print(void) {cout << "storage = " << storage;}
};

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get() + 1);
    object.print();
    return 0;
}

Como puede ver en el código de ejemplo, tenemos una nueva funcionalidad para la Subclase y hace una cosa importante: accede a la variable de almacenamiento desde la clase Super .

No sería posible si la variable se declarara como privada. En el ámbito de la función principal, la variable permanece oculta de todos modos, así que si escribe algo como:

object.storage = 0;

El compilador le informará que es un error: 'int Super::storage' is protected.

Finalmente, el último programa producirá el siguiente resultado:

storage = 101
BugShotGG
fuente
44
El primero en mencionar la falta de un modificador (como en Class: SuperClass) rinde privado. Esta es una pieza importante que los demás se están perdiendo, junto con explicaciones detalladas. +1
Agua
2
Overkill IMO, pero me gusta la mesa al principio.
cp.engr
63

Tiene que ver con cómo los miembros públicos de la clase base están expuestos de la clase derivada.

  • public -> los miembros públicos de la clase base serán públicos (generalmente el predeterminado)
  • protegido -> los miembros públicos de la clase base estarán protegidos
  • privado -> los miembros públicos de la clase base serán privados

Como señala Litb, la herencia pública es una herencia tradicional que verá en la mayoría de los lenguajes de programación. Es decir, modela una relación "IS-A". La herencia privada, algo AFAIK peculiar de C ++, es una relación "IMPLEMENTADA EN TÉRMINOS DE". Es decir, desea utilizar la interfaz pública en la clase derivada, pero no desea que el usuario de la clase derivada tenga acceso a esa interfaz. Muchos argumentan que, en este caso, debe agregar la clase base, es decir, en lugar de tener la clase base como base privada, hacer un miembro derivado para reutilizar la funcionalidad de la clase base.

Doug T.
fuente
13
Mejor diga "público: la herencia será vista por todos". protegido: la herencia solo será vista por clases derivadas y amigos "," privado: la herencia solo será vista por la clase misma y amigos ". Esto es diferente de su redacción, ya que no solo los miembros pueden ser invisibles, sino también el IS-a relación puede ser invisible.
Johannes Schaub - litb
44
La única vez que usé la herencia privada fue para hacer justo lo que Doug T describe, es decir, "desea utilizar la interfaz pública en la clase derivada, pero no desea que el usuario de la clase derivada tenga acceso a esa interfaz". Básicamente lo usé para sellar la interfaz anterior y exponer otra a través de la clase derivada.
Rico
36
Member in base class : Private   Protected   Public   

Tipo de herencia :              Objeto heredado como :

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
kinshuk4
fuente
23
Esto es engañoso. Los miembros privados de una clase base se comportan de manera bastante diferente a los miembros de la clase privada ordinaria: no son accesibles desde la clase derivada en absoluto. Creo que su columna de tres "Privado" debería ser una columna de "Inaccesible". Ver la respuesta de Kirill V. Lyadvinsky a esta pregunta.
Sam Kauffman
27

1) Herencia pública :

a. Los miembros privados de la clase Base no son accesibles en la clase Derivada.

si. Los miembros protegidos de la clase Base permanecen protegidos en la clase Derivada.

C. Los miembros públicos de la clase Base siguen siendo públicos en la clase Derivada.

Por lo tanto, otras clases pueden usar miembros públicos de la clase Base a través del objeto de clase Derivado.

2) Herencia protegida :

a. Los miembros privados de la clase Base no son accesibles en la clase Derivada.

si. Los miembros protegidos de la clase Base permanecen protegidos en la clase Derivada.

C. Los miembros públicos de la clase Base también se convierten en miembros protegidos de la clase Derivada.

Por lo tanto, otras clases no pueden usar miembros públicos de la clase Base a través del objeto de clase Derivado; pero están disponibles para la subclase de Derivado.

3) Herencia privada :

a. Los miembros privados de la clase Base no son accesibles en la clase Derivada.

si. Los miembros protegidos y públicos de la clase Base se convierten en miembros privados de la clase Derivada.

Por lo tanto, ningún miembro de la clase Base puede acceder a otras clases a través del objeto de clase Derivado, ya que son privadas en la clase Derivada. Entonces, incluso la subclase de la clase Derivada no puede acceder a ellos.

Yuvi
fuente
20

La herencia pública modela una relación IS-A. Con

class B {};
class D : public B {};

todo D es un B .

La herencia privada modela una relación de USO IMPLEMENTADO (o como se llame). Con

class B {};
class D : private B {};

a noD es a , pero cada usa su en su implementación. La herencia privada siempre se puede eliminar utilizando la contención en su lugar:BDB

class B {};
class D {
  private: 
    B b_;
};

Esto Dtambién se puede implementar usando B, en este caso usando sub_ . La contención es un acoplamiento menos estrecho entre los tipos que la herencia, por lo que en general debería preferirse. A veces, usar la contención en lugar de la herencia privada no es tan conveniente como la herencia privada. A menudo es una excusa poco convincente para ser flojo.

No creo que nadie sepa qué protectedmodelos de herencia. Al menos aún no he visto ninguna explicación convincente.

sbi
fuente
Algunos dicen un como una relación. Como usar la silla como martillo. Aquí silla: martillo protegido
usuario4951
cuando usar contención en lugar de herencia privada no es tan conveniente como la herencia privada? ¿Podría explicarlo con un ejemplo?
Destructor
@Pravasi: si se Dderiva de forma privada D, puede anular las funciones virtuales de B. (Si, por ejemplo, Bes una interfaz de observador, entonces Dpodría implementarla y pasar thisa funciones que requieren auch una interfaz, sin que todos puedan usarla Dcomo observador). Además, Dpodría hacer que los miembros Bestén disponibles selectivamente en su interfaz haciendo using B::member. Ambos son sintácticamente inconvenientes de implementar cuando Bes miembro.
sbi
@sbi: uno antiguo pero ... la contención es una opción prohibida en el caso de CRTP y / o virtuales (como describió correctamente en el comentario, pero eso significa que no se puede modelar como contención si B tiene métodos abstractos y usted no está permitido tocarlo). protectedherencia que he encontrado útil con una virtualclase base y protectedctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
lorro
11

Si hereda públicamente de otra clase, todos saben que está heredando y cualquiera puede usarlo polimórficamente a través de un puntero de clase base.

Si hereda de forma protegida, solo sus clases de niños podrán usarlo polimórficamente.

Si hereda en privado, solo usted podrá ejecutar métodos de clase principal.

Lo que básicamente simboliza el conocimiento que el resto de las clases tienen sobre su relación con la clase de sus padres.

Arkaitz Jiménez
fuente
9

Los miembros de datos protegidos pueden acceder a cualquier clase que herede de su clase. Los miembros de datos privados, sin embargo, no pueden. Digamos que tenemos lo siguiente:

class MyClass {
    private:
        int myPrivateMember;    // lol
    protected:
        int myProtectedMember;
};

Desde su extensión a esta clase, las referencias this.myPrivateMemberno funcionarán. Sin embargo, lo this.myProtectedMemberhará. El valor todavía está encapsulado, por lo que si tenemos una instanciación de esta clase llamada myObj, entonces myObj.myProtectedMemberno funcionará, por lo que es similar en función a un miembro de datos privado.

Andrew Noyes
fuente
8
Accessors    | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public       |      y     |       y       |   y
—————————————+————————————+———————————————+———————
protected    |      y     |       y       |   n
—————————————+————————————+———————————————+———————
private      |            |               |    
  or         |      y     |       n       |   n
no accessor  |            |               |

y: accessible
n: not accessible

Basado en este ejemplo para Java ... creo que una pequeña mesa vale más que mil palabras :)

Enissay
fuente
Java solo tiene herencia pública
Zelldon
Este no es el tema para hablar sobre Java, pero NO, te equivocas ... Sigue el enlace en mi respuesta anterior para más detalles
Enissay
Mencionaste Java, así que es el tema. Y su ejemplo maneja los especificadores que usan en jaca. La pregunta es acerca de los especificadores de herencia que no existen en Java y que marcaron la diferencia. Si un campo en la superclase es público y la herencia es privada, el campo solo es accesible dentro de la subclase. Afuera no hay indicación si la subclase extiende la superclase. Pero su tabla solo explica los especificadores de campo y métodos.
Zelldon
7

Resumen:

  • Privado: nadie puede verlo excepto dentro de la clase
  • Protegido: las clases privadas + derivadas pueden verlo
  • Público: el mundo puede verlo

Al heredar, puede (en algunos idiomas) cambiar el tipo de protección de un miembro de datos en cierta dirección, por ejemplo, de protegido a público.

Roee Adler
fuente
6

Privado:

A los miembros privados de una clase base solo pueden acceder miembros de esa clase base.

Público:

Los miembros públicos de una clase base pueden ser accedidos por miembros de esa clase base, miembros de su clase derivada, así como los miembros que están fuera de la clase base y la clase derivada.

Protegido:

Los miembros protegidos de una clase base pueden ser accedidos por los miembros de la clase base, así como por los miembros de su clase derivada.


En breve:

privado : base

protegido : base + derivado

public : base + derivado + cualquier otro miembro

varun
fuente
5

Encontré una respuesta fácil y pensé en publicarla también para mi futura referencia.

Es de los enlaces http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/

class Base
{
public:
    int m_nPublic; // can be accessed by anybody
private:
    int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
    int m_nProtected; // can be accessed by Base member functions, or derived classes.
};

class Derived: public Base
{
public:
    Derived()
    {
        // Derived's access to Base members is not influenced by the type of inheritance used,
        // so the following is always true:

        m_nPublic = 1; // allowed: can access public base members from derived class
        m_nPrivate = 2; // not allowed: can not access private base members from derived class
        m_nProtected = 3; // allowed: can access protected base members from derived class
    }
};

int main()
{
    Base cBase;
    cBase.m_nPublic = 1; // allowed: can access public members from outside class
    cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
    cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
Prajosh Premdas
fuente
3

Es esencialmente la protección de acceso de los miembros públicos y protegidos de la clase base en la clase derivada. Con la herencia pública, la clase derivada puede ver miembros públicos y protegidos de la base. Con herencia privada, no puede. Con protegido, la clase derivada y cualquier clase derivada de eso pueden verlos.

Dan Olson
fuente