Diferença entre herança privada, pública e protegida

1013

Qual é a diferença entre public, privatee protectedherança em C ++?

Todas as perguntas que encontrei no SO tratam de casos específicos.

Arsen Khachaturyan
fonte

Respostas:

1065

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, protectede private.

Deixei:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Tudo o que está ciente Basetambém está ciente de que Basecontém publicMember.
  • Somente as crianças (e seus filhos) estão cientes de que Basecontém protectedMember.
  • Ninguém, mas Baseestá ciente privateMember.

Por "está ciente de", quero dizer "reconheça a existência e, portanto, seja capaz de acessar".

Próximo:

O mesmo acontece com a herança pública, privada e protegida. Vamos considerar uma classe Basee uma classe Childque herda Base.

  • Se a herança é public, tudo o que está ciente Basee Childtambém está ciente que Childherda Base.
  • Se a herança é protectedapenas Child, e seus filhos, estão cientes de que eles herdam Base.
  • Se a herança é private, ninguém além disso Childestá ciente da herança.
Anzurio
fonte
183
Gostaria de acrescentar algumas palavras que a visibilidade no C ++ é baseada na classe, e não no objeto, o que significa que objetos da mesma classe podem acessar os campos privados um do outro sem restrição.
Zhe Chen
48
Se você tiver dificuldade para entender isso, leia a resposta de Kirill V. Lyadvinsky e volte a ler.
O Vivandiere
6
Este é apenas outro caso que ilustra como, na maioria das vezes, herdar de 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.
Underscore_d
1
@ZheChen se eu tiver objetos Tom e Jerry da classe Pessoa com idade de campo privada, como você acessa (e modifica?) A idade de Jerry usando Tom?
gen
2
Você poderia ilustrar o que você entende por "ciente da 'herança'"? Eu consigo entender "eu posso acessar isso, não consigo acessar isso", mas não entendo quando alguém diz "sei que A herda de B", o que estou fazendo aqui? Estou verificando a herança?
neilxdims
1458
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 .

Kirill V. Lyadvinsky
fonte
35
O que Anzurio escreveu apenas clicou em conjunto com sua resposta imediatamente abaixo. Exemplo 1. #
Iwillnotexist Idonotexist # / 4/15
2
Minha compreensão de como isso funcionou foi tão longe! Muito obrigado por esclarecer.
precisa saber é o seguinte
Levei algum tempo para entender isso. Mas agora está claro. Obrigado!
Chan Kim
115

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

  1. 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

  1. Implementado protegido em termos de. Raramente útil. Usado boost::compressed_pairpara 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

  1. 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

  1. Agregar

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

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

membro protegido

  1. Fornecendo acesso aprimorado para classes derivadas

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

membro privado

  1. 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.

Johannes Schaub - litb
fonte
7
Eu acho que Scott Myers (por mais que eu goste das coisas dele) tem muito a responder pela confusão geral. Agora acho que suas analogias com IS-A e IS-IMPLEMENTED-IN-TERMMS-OF são suficientes para o que está acontecendo.
14138 DangerMouse
65

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.

insira a descrição da imagem aqui

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, rna 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, zna 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 Superou essa Subclasse é derivada da Superclasse. A Subclasse não apresenta novas variáveis ​​nem novas funções. Isso significa que qualquer objeto da Subclasse herda todas as características após a Superclasse 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 puteget 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 Subclasse podem fazer "quase" as mesmas coisas que seus irmãos mais velhos criados a partir da Superclasse. "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 Subclasse 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 Subclasse 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
BugShotGG
fonte
4
O primeiro a mencionar a falta de um modificador (como em Class: SuperClass) produz private. Esta é uma peça importante que os outros estão perdendo, juntamente com explicações completas. +1
Água
2
Exagerar na IMO, mas eu gosto da mesa no começo.
#
63

Tem a ver com a maneira como os membros públicos da classe base são expostos a partir da classe derivada.

  • público -> os membros públicos da classe base serão públicos (geralmente o padrão)
  • protected -> os membros públicos da classe base serão protegidos
  • private -> os membros públicos da classe base serão privados

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.

Doug T.
fonte
13
Melhor dizer "público: a herança será vista por todos". protected: a herança será vista apenas por classes e amigos derivados "," private: a herança será vista apenas pela própria classe e amigos ". Isso é diferente da sua redação, pois não apenas os membros podem ser invisíveis, mas também a relação IS-A pode ser invisível
Johannes Schaub - litb
4
A única vez que usei herança privada foi fazer exatamente o que Doug T descreve, 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". Basicamente, usei-o para isolar a interface antiga e expor outra através da classe derivada.
Rich
36
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
kinshuk4
fonte
23
Isso é enganador. Os membros privados de uma classe base se comportam de maneira bem diferente dos membros comuns da classe privada - eles não são acessíveis a partir da classe derivada. Eu acho que sua coluna de três "Particular" deve ser uma coluna de "Inacessível". Veja a resposta de Kirill V. Lyadvinsky a esta pergunta.
Sam Kauffman
27

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.

yuvi
fonte
20

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:BDB

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

Isso Dtambé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 protectedmodelos de herança. Pelo menos ainda não vi nenhuma explicação convincente.

sbi
fonte
Alguns dizem que um relacionamento. Como usar cadeira como um martelo. Aqui cadeira: hammer protegido
user4951 28/11
quando usar contenção em vez de herança privada não é tão conveniente quanto a herança privada? Você poderia explicar usando um exemplo?
Destructor
@Ravasi: se Dderiva de maneira particular D, ele pode substituir as funções virtuais do B. (Se, por exemplo, Bfor uma interface de observador, é Dpossível implementá-la e passar thispara funções que exigem essa interface, sem que todos possam usar Dcomo observador.) Além disso, também Dpoderia disponibilizar membros de maneira seletiva Bem sua interface using B::member. Ambos são sintaticamente inconvenientes para implementar quando Bé um membro.
Sbi # 6/15
@sbi: antigo, mas ... a contenção é proibida no caso de CRTP e / ou virtuais (como você descreveu corretamente no comentário - mas isso significa que não pode ser modelado como contenção se B tiver métodos abstratos e você não é permitido tocá-lo). protectedherança que eu encontrei útil com uma virtualclasse base e 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

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

Arkaitz Jimenez
fonte
9

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.myPrivateMembernão funcionará. No entanto, this.myProtectedMembervontade. O valor ainda está encapsulado, portanto, se tivermos uma instanciação dessa classe chamada myObj, myObj.myProtectedMembernão funcionará, portanto, é semelhante em função a um membro de dados privado.

Andrew Noyes
fonte
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

Baseado neste exemplo para java ... acho que uma mesinha vale mais que mil palavras :)

Enissay
fonte
Java tem herança única pública
Zelldon
Este não é o tópico para falar sobre java, mas Não, você está errado ... Siga o link na minha resposta acima para detalhes
Enissay
Você mencionou java, então esse é o tópico. E seu exemplo lida com os especificadores que usam em jaca. A questão é sobre os especificadores de herança que não existem em Java e fizeram a diferença. Se um campo na superclasse for público e a herança for privada, o campo será acessível apenas dentro da subclasse. Lá fora, não há indicação se a subclasse estende a superclasse. Mas sua tabela explica apenas os especificadores de campo e métodos.
Zelldon
7

Resumo:

  • Privado: ninguém pode vê-lo, exceto dentro da classe
  • Protegido: Privado + classes derivadas podem vê-lo
  • Público: o mundo pode vê-lo

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.

Roee Adler
fonte
6

Privado:

Os membros privados de uma classe base só podem ser acessados ​​por membros dessa classe base.

Público:

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.

Protegido:

Os membros protegidos de uma classe base podem ser acessados ​​por membros da classe base e por membros de sua classe derivada.


Em resumo:

privado : base

protegido : base + derivado

público : base + derivado + qualquer outro membro

varun
fonte
5

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
}
Prajosh Premdas
fonte
3

É 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.

Dan Olson
fonte