Qual é a diferença entre
public
,private
eprotected
herança em C ++?
Todas as perguntas que encontrei no SO tratam de casos específicos.
Qual é a diferença entre
public
,private
eprotected
herança em C ++?
Todas as perguntas que encontrei no SO tratam de casos específicos.
Respostas:
Para responder a essa pergunta, gostaria de descrever os acessadores dos membros primeiro com minhas próprias palavras. Se você já sabe disso, pule para o título "próximo:".
Há três assessores que eu estou ciente de: public
, protected
e private
.
Deixei:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
Base
também está ciente de que Base
contém publicMember
.Base
contém protectedMember
.Base
está ciente privateMember
.Por "está ciente de", quero dizer "reconheça a existência e, portanto, seja capaz de acessar".
O mesmo acontece com a herança pública, privada e protegida. Vamos considerar uma classe Base
e uma classe Child
que herda Base
.
public
, tudo o que está ciente Base
e Child
também está ciente que Child
herda Base
.protected
apenas Child
, e seus filhos, estão cientes de que eles herdam Base
.private
, ninguém além disso Child
está ciente da herança.SomeBase
é como uma maneira codificada de compor em um membro anônimo do tipo SomeBase
. Este, como qualquer outro membro, possui um especificador de acesso, que exerce o mesmo controle no acesso externo.
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: As classes B, C e D contêm todas as variáveis x, ye z. É apenas uma questão de acesso.
Sobre o uso da herança protegida e privada, você pode ler aqui .
Limitar a visibilidade da herança tornará o código incapaz de ver que alguma classe herda outra classe: conversões implícitas da derivada para a base não funcionarão e static_cast
da base para a derivada também não funcionará.
Somente membros / amigos de uma classe podem ver herança privada, e somente membros / amigos e classes derivadas podem ver herança protegida.
herança pública
Herança IS-A. Um botão é uma janela e, em qualquer lugar em que uma janela seja necessária, também pode ser passado um botão.
class button : public window { };
herança protegida
Implementado protegido em termos de. Raramente útil. Usado boost::compressed_pair
para derivar de classes vazias e economizar memória usando a otimização de classe base vazia (o exemplo abaixo não usa modelo para continuar sendo o ponto):
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!
}
};
herança privada
Implementado em termos de. O uso da classe base é apenas para implementar a classe derivada. Útil com características e se o tamanho importa (características vazias que contêm apenas funções farão uso da otimização da classe base vazia). Muitas vezes , porém, a contenção é a melhor solução. O tamanho das strings é crítico, por isso é um uso frequentemente visto aqui
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
membro público
Agregar
class pair {
public:
First first;
Second second;
};
Accessors
class window {
public:
int getWidth() const;
};
membro protegido
Fornecendo acesso aprimorado para classes derivadas
class stack {
protected:
vector<element> c;
};
class window {
protected:
void registerClass(window_descriptor w);
};
membro privado
Manter detalhes da implementação
class window {
private:
int width;
};
Observe que as projeções no estilo C permitem propositalmente converter uma classe derivada para uma classe base protegida ou privada de uma maneira definida e segura e também para a outra direção. Isso deve ser evitado a todo custo, porque pode tornar o código dependente dos detalhes da implementação - mas, se necessário, você pode fazer uso dessa técnica.
Essas três palavras-chave também são usadas em um contexto completamente diferente para especificar o modelo de herança de visibilidade .
Esta tabela reúne todas as combinações possíveis do modelo de herança e declaração de componentes, apresentando o acesso resultante aos componentes quando a subclasse é completamente definida.
A tabela acima é interpretada da seguinte maneira (veja a primeira linha):
se um componente é declarado como público e sua classe é herdada como pública, o acesso resultante é público .
Um exemplo:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
O acesso resultando em variáveis p
, q
, r
na classe subsub é nenhum .
Outro exemplo:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
O acesso resultante para variáveis y
, z
na classe Sub, é protegido e para a variável x
é none .
Um exemplo mais detalhado:
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;
}
Agora vamos definir uma subclasse:
class Sub : Super { };
int main(void) {
Sub object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
A classe definida denominada Sub, que é uma subclasse da classe denominada Super
ou essa Sub
classe é derivada da Super
classe. A Sub
classe não apresenta novas variáveis nem novas funções. Isso significa que qualquer objeto da Sub
classe herda todas as características após a Super
classe ser de fato uma cópia de umSuper
objetos classe?
Não . Não faz.
Se compilarmos o código a seguir, não obteremos nada além de erros de compilação dizendo que put
eget
métodos são inacessíveis. Por quê?
Quando omitimos o especificador de visibilidade, o compilador supõe que vamos aplicar a chamada herança privada . Isso significa que todos os componentes públicos da superclasse se tornam privados o acesso, os componentes da superclasse privadas não será acessível a todos. Consequentemente, significa que você não tem permissão para usá-lo dentro da subclasse.
Temos que informar ao compilador que queremos preservar a política de acesso usada anteriormente.
class Sub : public Super { };
Não se deixe enganar : isso não significa que os componentes privados da classe Super (como a variável de armazenamento) se transformarão em públicos de uma maneira um tanto mágica. Os componentes privados permanecerão privados , o público permanecerá público .
Objetos da Sub
classe podem fazer "quase" as mesmas coisas que seus irmãos mais velhos criados a partir da Super
classe. "Quase" porque o fato de ser uma subclasse também significa que a classe perdeu o acesso aos componentes privados da superclasse . Não podemos escrever uma função membro da Sub
classe que seria capaz de manipular diretamente a variável de armazenamento.
Esta é uma restrição muito séria. Existe alguma solução alternativa?
Sim .
O terceiro nível de acesso é chamado de protegido . A palavra-chave protegida significa que o componente marcado com ela comporta como um público quando usado por qualquer uma das subclasses e se parece com um privado para o resto do mundo . - Isso é verdade apenas para as classes herdadas público (como a classe Super no nosso exemplo) -
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 você pode ver no código de exemplo, temos uma nova funcionalidade para a Sub
classe e ela faz uma coisa importante: acessa a variável de armazenamento da classe Super .
Não seria possível se a variável fosse declarada como privada. No escopo da função principal, a variável permanece oculta mesmo assim, se você escrever algo como:
object.storage = 0;
O compilador irá informá-lo que é um error: 'int Super::storage' is protected
.
Por fim, o último programa produzirá a seguinte saída:
storage = 101
Tem a ver com a maneira como os membros públicos da classe base são expostos a partir da classe derivada.
Como o litb aponta, a herança pública é uma herança tradicional que você verá na maioria das linguagens de programação. É isso que modela um relacionamento "IS-A". A herança privada, algo que o AFAIK peculiar ao C ++, é um relacionamento "IMPLEMENTADO EM TERMOS DE". Ou seja, você deseja usar a interface pública na classe derivada, mas não deseja que o usuário da classe derivada tenha acesso a essa interface. Muitos argumentam que, nesse caso, você deve agregar a classe base, ou seja, em vez de ter a classe base como uma base privada, crie um membro derivado para reutilizar a funcionalidade da classe base.
Member in base class : Private Protected Public
Tipo de herança : Objeto herdado como :
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1) Herança Pública :
uma. Membros privados da classe Base não estão acessíveis na classe Derivada.
b. Membros protegidos da classe Base permanecem protegidos na classe Derived.
c. Os membros públicos da classe Base permanecem públicos na classe Derived.
Portanto, outras classes podem usar membros públicos da classe Base por meio do objeto de classe Derived.
2) Herança Protegida :
uma. Membros privados da classe Base não estão acessíveis na classe Derivada.
b. Membros protegidos da classe Base permanecem protegidos na classe Derived.
c. Os membros públicos da classe Base também se tornam membros protegidos da classe Derived.
Portanto, outras classes não podem usar membros públicos da classe Base através do objeto de classe Derived; mas eles estão disponíveis para a subclasse de Derivado.
3) Herança Privada :
uma. Membros privados da classe Base não estão acessíveis na classe Derivada.
b. Membros protegidos e públicos da classe Base tornam-se membros privados da classe Derived.
Portanto, nenhum membro da classe Base pode ser acessado por outras classes por meio do objeto de classe Derived, pois são particulares na classe Derived. Portanto, mesmo a subclasse da classe Derived não pode acessá-los.
A herança pública modela um relacionamento IS-A. Com
class B {};
class D : public B {};
tudo D
é um B
.
A herança privada modela um relacionamento IS-IMPLEMENTED-USING (ou o que for chamado). Com
class B {};
class D : private B {};
a nãoD
é a , mas todos os usam em sua implementação. A herança privada sempre pode ser eliminada usando a contenção:B
D
B
class B {};
class D {
private:
B b_;
};
Isso D
também pode ser implementado usando B
, neste caso, usando seub_
. A contenção é um acoplamento menos rígido entre os tipos do que a herança, portanto, em geral, deve ser preferido. Às vezes, usar a contenção em vez da herança privada não é tão conveniente quanto a herança privada. Muitas vezes isso é uma desculpa esfarrapada por ser preguiçoso.
Acho que ninguém sabe que protected
modelos de herança. Pelo menos ainda não vi nenhuma explicação convincente.
D
deriva de maneira particular D
, ele pode substituir as funções virtuais do B
. (Se, por exemplo, B
for uma interface de observador, é D
possível implementá-la e passar this
para funções que exigem essa interface, sem que todos possam usar D
como observador.) Além disso, também D
poderia disponibilizar membros de maneira seletiva B
em sua interface using B::member
. Ambos são sintaticamente inconvenientes para implementar quando B
é um membro.
protected
herança que eu encontrei útil com uma virtual
classe base e protected
ctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
Se você herdar publicamente de outra classe, todos saberão que você está herdando e você poderá ser usado polimorficamente por qualquer pessoa através de um ponteiro de classe base.
Se você herdar de forma protegida, apenas as classes de seus filhos poderão usá-lo polimorficamente.
Se você herdar privadamente, somente você poderá executar os métodos da classe pai.
O que basicamente simboliza o conhecimento que o restante das classes tem sobre o seu relacionamento com a classe dos pais
Os membros de dados protegidos podem ser acessados por quaisquer classes herdadas da sua classe. Membros de dados privados, no entanto, não podem. Digamos que temos o seguinte:
class MyClass {
private:
int myPrivateMember; // lol
protected:
int myProtectedMember;
};
De dentro da sua extensão para esta classe, a referência this.myPrivateMember
não funcionará. No entanto, this.myProtectedMember
vontade. O valor ainda está encapsulado, portanto, se tivermos uma instanciação dessa classe chamada myObj
, myObj.myProtectedMember
não funcionará, portanto, é semelhante em função a um membro de dados privado.
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
Baseado neste exemplo para java ... acho que uma mesinha vale mais que mil palavras :)
Resumo:
Ao herdar, você pode (em alguns idiomas) alterar o tipo de proteção de um membro de dados em determinada direção, por exemplo, de protegido para público.
Os membros privados de uma classe base só podem ser acessados por membros dessa classe base.
Os membros públicos de uma classe base podem ser acessados por membros dessa classe base, membros de sua classe derivada, bem como membros que estão fora da classe base e da classe derivada.
Os membros protegidos de uma classe base podem ser acessados por membros da classe base e por membros de sua classe derivada.
privado : base
protegido : base + derivado
público : base + derivado + qualquer outro membro
Encontrei uma resposta fácil e pensei em publicá-la para minha referência futura também.
É dos links 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
}
É essencialmente a proteção de acesso dos membros públicos e protegidos da classe base na classe derivada. Com a herança pública, a classe derivada pode ver membros públicos e protegidos da base. Com herança privada, não pode. Com protected, a classe derivada e quaisquer classes derivadas disso podem vê-los.