Programar em C++/Herança: diferenças entre revisões

Origem: Wikilivros, livros abertos por um mundo aberto.
[edição não verificada][edição não verificada]
Conteúdo apagado Conteúdo adicionado
Linha 27: Linha 27:
</source>
</source>


Repare que temos o operador ''':''' como elo de ligação entre as duas classes. Este operador promove o "parentesco" entre as duas classes quando é usado na declaração de uma classe derivada.
Repare que temos o operador "''':'''" ( dois pontos ) como elo de ligação entre as duas classes. Este operador promove o "parentesco" entre as duas classes quando é usado na declaração de uma classe derivada.
O termo [<acesso>] é opcional, mas se estiver presente deve ser public, private ou protected. Ele define o grau de visibilidade dos membros da classe base quando a classe derivada precisar acessá-los.
O termo [<acesso>] é opcional, mas se estiver presente deve ser public, private ou protected. Ele define o grau de visibilidade dos membros da classe base quando a classe derivada precisar acessá-los.
Linha 33: Linha 33:
Exemplo de implementação:
Exemplo de implementação:
<source lang="cpp">
<source lang="cpp">
// Demonstrate inheritance.
// Demonstra herança.
#include <iostream>
#include <iostream>
using namespace std;
using namespace std;
class road_vehicle // Define a base class for vehicles.
class veiculo_rodoviario // Define uma classe base veículos.
{
{
int wheels;
int rodas;
int passengers;
int passageiros;
public:
public:
void set_wheels(int num) { wheels = num; }
void set_rodas(int num) { rodas = num; }
int get_wheels() { return wheels; }
int get_rodas() { return rodas; }
void set_pass(int num) { passengers = num; }
void set_pass(int num) { passageiros = num; }
int get_pass() { return passengers; }
int get_pass() { return passageiros; }
};
};
class truck : public road_vehicle // Define a truck.
class caminhao : public veiculo_rodoviario // Define um caminhao.
{
{
int cargo;
int carga;
public:
public:
void set_cargo(int size) { cargo = size; }
void set_carga(int size) { carga = size; }
int get_cargo() { return cargo; }
int get_carga() { return carga; }
void show();
void mostrar();
};
};
enum type {car, van, wagon};
enum tipo {carro, van, vagao};
class automobile : public road_vehicle // Define an automoble.
class automoveis : public veiculo_rodoviario // Define an automoble.
{
{
enum type car_type;
enum tipo car_tipo;
public:
public:
void set_type(type t) { car_type = t; }
void set_tipo(tipo t) { car_tipo = t; }
enum type get_type() { return car_type; }
enum tipo get_tipo() { return car_tipo; }
void show();
void mostrar();
};
};
void truck::show()
void caminhao::mostrar()
{
{
cout << "wheels: " << get_wheels() << "\n";
cout << "rodas: " << get_rodas() << "\n";
cout << "passengers: " << get_pass() << "\n";
cout << "passageiros: " << get_pass() << "\n";
cout << "cargo capacity in liters: " << cargo << "\n";
cout << "carga (capacidade em litros): " << carga << "\n";
}
}
void automobile::show()
void automoveis::mostrar()
{
{
cout << "wheels: " << get_wheels() << "\n";
cout << "rodas: " << get_rodas() << "\n";
cout << "passengers: " << get_pass() << "\n";
cout << "passageiros: " << get_pass() << "\n";
cout << "type: ";
cout << "tipo: ";
switch(get_type())
switch(get_tipo())
{
{
case van: cout << "van\n";
case van: cout << "van\n";
break;
break;
case car: cout << "car\n";
case car: cout << "carro\n";
break;
break;
case wagon: cout << "wagon\n";
case vagao: cout << "vagao\n";
}
}
}
}
int main()
int main()
{
{
truck t1, t2;
caminhao t1, t2;
automobile c;
automoveis c;
t1.set_wheels(18);
t1.set_rodas(18);
t1.set_pass(2);
t1.set_pass(2);
t1.set_cargo(3200);
t1.set_carga(3200);
t2.set_wheels(6);
t2.set_rodas(6);
t2.set_pass(3);
t2.set_pass(3);
t2.set_cargo(1200);
t2.set_carga(1200);
t1.show();
t1.mostrar();
cout << "\n";
cout << "\n";
t2.show();
t2.mostrar();
cout << "\n";
cout << "\n";
c.set_wheels(4);
c.set_rodas(4);
c.set_pass(6);
c.set_pass(6);
c.set_type(van);
c.set_tipo(van);
c.show();
c.mostrar();
#ifdef WIN32
system ("pause");
system ("pause");
#endif
return 0;
return 0;
}
}

Revisão das 21h04min de 23 de janeiro de 2010

<< Classes Encapsulamento >>
Esta página precisa ser reciclada (discuta).
Ao melhorá-la, você estará ajudando o Wikilivros.

Conceito

Herança é um dos pontos chave de programação orientada a objetos (POO). Ela fornece meios de promover a extensibilidade do código, a reutilização e uma maior coerência lógica no modelo de implementação. Estas características nos possibilitam diversas vantagens, principalmente quando o mantemos bibliotecas para uso futoro de determinados recursos que usamos com muita frequência.

Uma classe de objetos "veiculo", por exemplo, contém todas as características inerentes aos veículos, como: combustível, autonomia, velocidade máxima, etc. Agora podemos dizer que "carro" é uma classe que têm as características básicas da classe "veículo" mais as suas características particulares. Analisando esse fato, podemos concluir que poderíamos apenas definir em "carro" suas características e usar "veículo" de alguma forma que pudéssemos lidar com as características básicas. Este meio chama-se herança.

Agora podemos definir outros tipos de veículos como: moto, caminhão, trator, helicóptero, etc, sem ter que reescrever a parte que está na classe "veículo". Para isso define-se a classe "veículo" com suas características e depois cria-se classes específicas para cada veículo em particular, declarando-se o parentesco neste instante.

Outro exemplo: Imagine que já exista uma classe que defina o comportamento de um dado objeto da vida real, por exemplo, animal. Uma vez que eu sei que o leão é um animal, o que se deve fazer é aproveitar a classe animal e fazer com que a classe leão derive (herde) da classe animal as características e comportamentos que a mesma deve apresentar, que são próprios dos indivíduos classificados como animais.

Ou seja, herança acontece quando duas classes são próximas, têm características mútuas mas não são iguais e existe uma especificação de uma delas. Portanto, em vez de escrever todo o código novamente é possível poupar algum tempo e dizer que uma classe herda da outra e depois basta escrever o código para a especificação dos pontos necessários da classe derivada (classe que herdou).

Sintaxe

Para declarar uma classe derivada de outra já existente, procedemos de forma a declarar o parentesco e o grau de visibilidade (acesso) que a classe derivada terá dos membros de sua classe base. Para isso seguimos o seguinte código sintático:

 class classe_derivada : [<acesso>] classe_base {
  //corpo da classe derivada
 }

Repare que temos o operador ":" ( dois pontos ) como elo de ligação entre as duas classes. Este operador promove o "parentesco" entre as duas classes quando é usado na declaração de uma classe derivada.

O termo [<acesso>] é opcional, mas se estiver presente deve ser public, private ou protected. Ele define o grau de visibilidade dos membros da classe base quando a classe derivada precisar acessá-los.

Exemplo de implementação:

 // Demonstra herança.
 #include <iostream>
 using namespace std;
 class veiculo_rodoviario // Define uma classe base veículos.
    {
        int rodas;
        int passageiros;
    public:
        void set_rodas(int num) { rodas = num; }
        int get_rodas() { return rodas; }
        void set_pass(int num) { passageiros = num; }
        int get_pass() { return passageiros; }
    };
 class caminhao : public veiculo_rodoviario // Define um caminhao.
    {
          int carga;
    public:
           void set_carga(int size) { carga = size; }
           int get_carga() { return carga; }
           void mostrar();
    };
 enum tipo {carro, van, vagao};
 class automoveis : public veiculo_rodoviario // Define an automoble.
    {
        enum tipo car_tipo;
    public:
        void set_tipo(tipo t) { car_tipo = t; }
        enum tipo get_tipo() { return car_tipo; }
        void mostrar();
    };
 void caminhao::mostrar()
        {
        cout << "rodas: " << get_rodas() << "\n";
        cout << "passageiros: " << get_pass() << "\n";
        cout << "carga (capacidade em litros): " << carga << "\n";
        }
 void automoveis::mostrar()
        {
        cout << "rodas: " << get_rodas() << "\n";
        cout << "passageiros: " << get_pass() << "\n";
        cout << "tipo: ";
        switch(get_tipo()) 
                {
                case van: cout << "van\n";
                          break;
                case car: cout << "carro\n";
                          break;
                case vagao: cout << "vagao\n";
                }
        }
 int main()
 {
    caminhao t1, t2;
    automoveis c;
    t1.set_rodas(18);
    t1.set_pass(2);
    t1.set_carga(3200);
    t2.set_rodas(6);
    t2.set_pass(3);
    t2.set_carga(1200);
    t1.mostrar();
    cout << "\n";
    t2.mostrar();
    cout << "\n";
    c.set_rodas(4);
    c.set_pass(6);
    c.set_tipo(van);
    c.mostrar();
#ifdef WIN32
    system ("pause");
#endif
    return 0;
 }

Na implementação acima temos a classe base road_vehicle e duas classes derivadas“:” truck e automobile.

Repare ainda um pormenor: tanto a classe truck quanto a automobile têm como método membro o método show(), mas uma não interfere com a outra. Isto ilustra um outro aspecto do polimorfismo.

Controle de acesso à classe base

Quando uma classe herda outra, os membros da classe base ficam membros da classe derivada. o acesso dos membros da classe base à classe derivada é determinado pelo especificador de acesso: public, private e protected. Por defaut temos o private, ou seja como temos a opção de não explicitar o especificador de acesso, sabes que eles são private por defaut.

Assim ficamos com as possíveis combinações

  • base class inheret as Public:
    • Public membrer da base classe:
      • É como tivéssemos a fazer copy dos public members da classe base e os colocássemos como public na classe derivada
    • Private membrer da base classe:
      • não são passados
    • Protected membrer da base classe:
      • se tivermos protected members na classe eles são como copiados para a classe derivada como protected.
  • base class inheret as Private:
    • Public membrer da base classe:

É como tivéssemos a fazer copy dos public members da classe base e os colocássemos como **private na classe derivada

    • Private membrer da base classe:
      • não são passados
    • Protected membrer da base classe:
      • è como estivéssemos a copiar os protected members da classe base e os colocássemos como private na classe derivada.


  • base class inheret as Protected:
    • Public membrer da base classe:
      • É como tivéssemos a fazer copy dos public members da classe base e os colocássemos como protected na classe derivada
    • Private membrer da base classe:
      • não são passados
    • Protected membrer da base classe:
      • Assim é como estivéssemos a copiar os protected members da classe base e os colocássemos como protected na classe derivada.



aqui está um exemplo muito simples

#include <iostream>
using namespace std;
class base 
{
     int i, j;
public:
      void set(int a, int b) { i = a; j = b; }
      void show() { cout << i << " " << j << "\n"; }
};
class derived : public base 
{
     int k;
public:
      derived(int x) { k = x; }
      void showk() { cout << k << "\n"; }
};
int main()
{
   derived ob(3);
   ob.set(1, 2); // access member of base
   ob.show(); // access member of base
   ob.showk(); // uses member of derived class
   system ("pause");
   return 0;
}


#include <iostream>
using namespace std;
class base 
{
     int i, j;
public:
      void set(int a, int b) { i = a; j = b; }
      void show() { cout << i << " " << j << "\n"; }
};
class derived : private base 
{
     int k;
public:
      derived(int x) { k = x; }
      void showk() { cout << k << "\n"; }
};
int main()
{
   derived ob(3);
   ob.set(1, 2); // Error, can't access set()
   ob.show(); // Error, can't access show()
   ob.showk(); // uses member of derived class
   system ("pause");
   return 0;
}

conseguimos aceder á função set() e show() porque é heradada como public Agora já não porque está como private

Heranças múltiplas

Podemos ter a situação em que uma classe possa herdar de várias classes base.

// An example of multiple base classes.
#include <iostream>
using namespace std;
class base1 
   {
   protected:
             int x;
   public:
          void showx() { cout << x << "\n"; }
   };
class base2 
   {
   protected:
             int y;
   public:
          void showy() { cout << y << "\n"; }
   };
class derived: public base1, public base2 // Inherit multiple base classes.
   {
   public:
          void set(int i, int j) { x = i; y = j; }
   };
int main()
{
   derived ob;
   ob.set(10, 20); // provided by derived
   ob.showx(); // from base1
   ob.showy(); // from base2
   system ("pause");
   return 0;
}

repare que utilizamos o operador comma (virgula) para dizer que herdamos várias classes.

Pergunta: e quando queremos herdar uma classe como public e outra como private ou protected. Basta preceder a classe com o seu especificador de acesso

Construtores e destrutores

Agora temos a questão quando é que os constructors são chamados quando eles são herdados?

  • quando um objecto da classe derivada é chamado, o constructor da base class é chamado primeiro seguido do constructor da classe derivada.
  • quando o objecto da classe derivada é destruído, o seu destructor é chamado primeiro seguido do destructor da base class

vamos ver isto. caso em que termos herança sequencial A-B-C

#include <iostream>
using namespace std;
class base 
   {
   public:
          base() { cout << "Constructing base\n"; }
          ~base() { cout << "Destructing base\n"; }
   };
class derived1 : public base 
   {
   public:
          derived1() { cout << "Constructing derived1\n"; }
          ~derived1() { cout << "Destructing derived1\n"; }
   };
class derived2: public derived1 
   {
   public:
          derived2() { cout << "Constructing derived2\n"; }
          ~derived2() { cout << "Destructing derived2\n"; }
   };
int main()
{
   derived2 ob; // construct and destruct ob
   system ("pause");
   return 0;
}

este exemplo está muito giro!!

caso de múltipla herança A- B e C

#include <iostream>
using namespace std;
class base1 
        {
        public:
              base1() { cout << "Constructing base1\n"; }
              ~base1() { cout << "Destructing base1\n"; }
       };
class base2 
       {
       public:
              base2() { cout << "Constructing base2\n"; }
              ~base2() { cout << "Destructing base2\n"; }
       };
class derived: public base2,public base1
       {
       public:
              derived() { cout << "Constructing derived\n"; }
              ~derived() { cout << "Destructing derived\n"; }
       }; 
int main()
{
   derived ob;// construct and destruct ob
   system ("pause");
   return 0;
}

repare que aqui é a ordem da esquerda para a direita que os constructors são executados na

Passando parâmetros para construtores da classe base

a sintax é:

derived-constructor(arg-list) : base1(arg-list), base2(arg-list), ...baseN(arg-list);
{
body of derived constructor
}


este exemplo está complexo, atenção!

#include <iostream>
using namespace std;
class base 
       {
       protected:
                 int i;
       public:
              base(int x) { i = x; cout << "Constructing base\n"; }
              ~base() { cout << "Destructing base\n"; }
       };
class derived: public base 
       {
             int j;
       public:
              derived(int x, int y): base(y) { j = x; cout << "Constructing derived\n"; }// derived uses x; y is passed along to base.
              ~derived() { cout << "Destructing derived\n"; }
              void show() { cout << i << " " << j << "\n"; }
       };
int main()
{
   derived ob(3, 4);
   ob.show(); // displays 4 3
   system ("pause");
   return 0;
}

aqui o derived constructor é declarado com 2 argumentos (x e y). no entanto a função derived() usa apenas um.


aqui vai mais um exemplo.

#include <iostream>
using namespace std;
class base1 
   {
   protected:
             int i;
   public:
          base1(int x) { i = x; cout << "Constructing base1\n"; }
          ~base1() { cout << "Destructing base1\n"; }
   };
class base2 
   {
   protected:
             int k;
   public:
          base2(int x) { k = x; cout << "Constructing base2\n"; }
          ~base2() { cout << "Destructing base2\n"; }
   };
class derived: public base1, public base2 
   {
        int j;
   public:
          derived(int x, int y, int z): base1(y), base2(z)
                      { j = x; cout << "Constructing derived\n"; }
          ~derived() { cout << "Destructing derived\n"; }
          void show() { cout << i << " " << j << " " << k << "\n"; }
   };
int main()
{
   derived ob(3, 4, 5);
   ob.show(); // displays 4 3 5
   system ("pause");
   return 0;
}

Controlando o acesso

recordam-se que os private members da classe base nunca são acedidos quer fora do programa quer mesmo sendo herdados. são apenas pelos restantes members da classe!

podemos no entanto conceder o mesmo nível de acesso que tinham na base classe aos membros herdados.

  • podemos conseguir isto através da palavra “using” e esta é a maneira recomendada, mas vamos deixar isto para o capitulo dos namespace
  • ou então usar um método que já está em desuso, que eu nem vou abordar.

Superposição de funções

Ocultando funções da classe base

Acessando funções superpostas da classe base

Funções virtuais

Chamando múltiplas funções virtuais

Funcões virtuais e passagem por valor

Construtor de cópia virtual

Classe base virtual

consideremos o seguinte programa:

// This program contains an error and will not compile.
#include <iostream>
using namespace std;
class base
   {
   public:
          int i;
   };
class derived1 : public base // derived1 inherits base.
   {
   public:
          int j;
   };
class derived2 : public base // derived2 inherits base.
   {
   public:
          int k;
   };
class derived3 : public derived1, public derived2 /* derived3 inherits both derived1 and derived2. This means that there are two copies of base in derived3! */
   {
   public: 
           int sum;
   };
int main()
{
   derived3 ob;
   ob.i = 10; // this is ambiguous; which i???
   ob.j = 20
   ob.k = 30;
   ob.sum = ob.i + ob.j + ob.k;// i ambiguous here, too
   cout << ob.i << " ";// also ambiguous, which i?
   cout << ob.j << " " << ob.k << " ";
   cout << ob.sum;
   system ("pause");
   return 0;
}

as classes derived1 e derived 2 herdam a classe base a classe derived3 herda quer derived 1 e derived2. como resultado temos 2 cópias da classe base presentes no objecto da derived 3. por exemplo presente na linha ob.i=20; isto resulta numa ambiguidade e o programa não vai compilar

há duas maneiras para remediar a situação 1. aplicar o operador scope resolution manualmente

// This program uses explicit scope resolution to select i.
#include <iostream>
using namespace std;
class base 
   {
   public:
          int i;
   };
class derived1 : public base // derived1 inherits base.
   {
   public:
          int j;
   };
class derived2 : public base // derived2 inherits base.
   {
   public:
          int k;
   };
class derived3 : public derived1, public derived2 /* derived3 inherits both derived1 and derived2. This means that there are two copies of base in derived3! */
   {
   public:
          int sum;
   };
int main()
{
   derived3 ob;
   ob.derived1::i = 10; 	// scope resolved, use derived1's i
   ob.j = 20;
   ob.k = 30;
   ob.sum = ob.derived1::i + ob.j + ob.k;		// scope resolved
   cout << ob.derived1::i << " ";		// also resolved here
   cout << ob.j << " " << ob.k << " ";
   cout << ob.sum;
   system ("pause");
   return 0;
}



2. a segunda maneira é através do virtual base classes. quando temos 2 ou mais objectos que são derivados da mesma base class, podemos prevenir múltiplas cópias da base class declarando a base class como virtual quando ela é herdada exemplificando

// This program uses virtual base classes.
#include <iostream>
using namespace std;
class base 
       {
       public:
              int i;
       };
class derived1 : virtual public base // derived1 inherits base as virtual.
       {
       public:
              int j;
       };
class derived2 : virtual public base // derived2 inherits base as virtual.
       {
       public:
              int k;
       };
class derived3 : public derived1, public derived2 /* derived3 inherits both derived1 and derived2. This time, there is only one copy of base class. */
       { 
       public:
              int sum;
       };
int main()
{
   derived3 ob;
   ob.i = 10; // now unambiguous
   ob.j = 20;
   ob.k = 30;
   ob.sum = ob.i + ob.j + ob.k;// unambiguous
   cout << ob.i << " ";// unambiguous
   cout << ob.j << " " << ob.k << " ";
   cout << ob.sum;
   system ("pause");
   return 0;
}

repare que agora temos a palavra virtual antes da classe

Ver também