2011-01-22 13 views
9

Soy C++ novato, y tengo muchos años de experiencia en lenguajes OO como C/C#/Objective-C. Ahora, estoy aprendiendo C++.Modificador de acceso de subclases C++?

vi este código C++:

class World : public State 
    { 
    }; 

Parece clase World hereda la clase State públicamente. ¿Subclase pública? Es difícil de entender.

¿Cuál es el concepto de esta función? ¿Y cuándo es esto útil o requerido?

+0

¿Quiso escribir 'clase World: private State {}'? En lo que realmente escribiste, la herencia es pública. –

+0

Quizás quiso decir 'class World: private State'? – detunized

+0

@Steve, @detunized Lo siento por mi estúpido error. Modifiqué la pregunta. – Eonil

Respuesta

13

La necesidad de la palabra clave public es solo eso para las clases definidas con la palabra clave class, el modificador de acceso predeterminado (para todo - miembros de datos, funciones de miembro y clases base) es private. Así

class World : State {}; 

es lo mismo que:

class World : private State {}; 

y que probablemente no es lo que quiere - que significa que la clase base sólo es accesible dentro de la clase World. Los forasteros "no saben" que la herencia está allí en absoluto.

Para las clases definidas con la palabra clave struct, el modificador de acceso por defecto es public, por lo que podía escritura:

struct World : State {}; 

y conseguir algo que tanto se ve y se comporta un poco como cualquier otro idioma con la herencia. Pero la palabra clave struct, y el hecho de que define una clase, realmente solo existe para compatibilidad con C.No encontrará muchas guías de estilo en C++ que recomienden usarlo solo para obtener la accesibilidad pública predeterminada: generalmente se usa solo para clases que son POD, o tal vez solo para clases sin funciones miembro.

En cuanto a por qué C++ tiene herencia privada en primer lugar: para la mayoría de los propósitos, la herencia privada es una forma de composición. composición normal:

class World { 
    State state; 
    public: 
    void foo() { 
     state.bar(); 
     state.baz(); 
     and so on 
    } 
}; 

Es decir, el mundo sabe que la clase se implementa usando un Estado, y el mundo exterior no sabe cómo se implementa Mundial.

vs

class World : private State { 
    public: 
    void foo() { 
     bar(); 
     baz(); 
     and so on 
    } 
}; 

Es decir, el Mundial de la clase sabe que es implementado por ser un Estado, y el mundo exterior no sabe cómo se implementa. Pero puede exponer selectivamente partes de la interfaz de estado, por ejemplo, poniendo using State::bar; en la parte pública de la definición de Mundo. El efecto es como si hubiera escrito laboriosamente una función (o varias sobrecargas) en World, cada una de las cuales delega a la misma función en State.

Además de evitar el tipeo, un uso común de la herencia privada es cuando la clase State está vacía, es decir, no tiene miembros de datos. Entonces, si es miembro de World debe ocupar un espacio (es cierto que, dependiendo del diseño del objeto, este podría ser un espacio que de lo contrario sería relleno, por lo que no aumenta necesariamente el tamaño de World), pero si es una clase base entonces se activa una cosa llamada "optimización de clase base vacía", y puede ser de tamaño cero. Si está creando un lote de objetos, esto podría importar. La herencia privada permite la optimización, pero el mundo exterior no deducirá una relación "is-a", porque no ve la herencia.

Es una gran diferencia: si tiene dudas, simplemente use la composición explícita. La introducción de la herencia para guardar la escritura está muy bien hasta que tenga alguna consecuencia inesperada.

+0

Gracias. Aceptado porque el último párrafo :) – Eonil

3

¿Qué te hace pensar que es privado? Dice pública allí, lo que significa que está subclasificando públicamente.

Aparte de eso, lo que hacen la herencia privada y protegida es lo mismo que la herencia pública, excepto que todas las variables miembro son funciones que se heredan con al menos accesibilidad privada o protegida. Por ejemplo, si State tuviera una función miembro pública 'foo()', sería privada en 'Mundo'.

Esto rara vez se usa en la práctica, pero tiene un propósito. El uso más común que he visto es la composición a través de la herencia. es decir, desea una relación "tiene una" en lugar de una "es una" (que generalmente obtiene con herencia pública). Al heredar de forma privada la clase, obtienes todas sus variables y métodos, pero no los expones al mundo exterior.

Una ventaja del uso de la herencia privada para la composición proviene de la optimización de la clase base vacía (EBCO). Usando la composición normal, tener un objeto miembro de una clase vacía todavía usaría al menos 1 byte porque todas las variables deben tener una dirección única. Si hereda en privado el objeto del que desea estar compuesto, entonces eso no se aplica y no sufrirá pérdida de memoria.

p. Ej.

class Empty { }; 

class Foo 
{ 
    int foo; 
    Empty e; 
}; 

class Bar : private Empty 
{ 
    int foo; 
}; 

Aquí, sizeof(Foo) probablemente será de 5, pero sizeof(Bar) habrá 4 debido a la clase base vacía.

+0

"lo mismo que la herencia pública, excepto que ...". La diferencia es más que eso. Además, los outsiders no pueden, por ejemplo, static_cast a 'World *' a 'State *'. –

+0

"sizeof (Foo) probablemente será 5" - o incluso peor, 8, si 'int' está alineado 4 en su implementación. –

+0

@Steve Jessp: cierto. –

4

En caso

class World: private State 
{ 
}; 

herencia privada significa que todos los publicprotected y miembros de State serían heredados por World y se convertirían en private. Esto sella State dentro de World. Ninguna clase que herede de World podrá acceder a las características de State.

0

La palabra clave public/protected/private antes del nombre de la clase ancestro indica la visibilidad deseada de los miembros del antecesor. Con la herencia privada, el descendiente hereda solo la implementación del antecesor, pero no la interfaz.

class A { 
public: 
    void foo(); 
}; 

class B : private A { 
public: 
    void bar(); 
}; 

void B::bar() 
{ 
    foo(); // can access foo() 
} 

B b; 
b.foo(); // forbidden 
b.bar(); // allowed 

En general, se debe utilizar la herencia pública porque la herencia no se debe utilizar para la implementación reutilización única (que es lo que hace la herencia privada).

Cuestiones relacionadas