Programar em C++/Herança: diferenças entre revisões
[edição não verificada] | [edição não verificada] |
Sem resumo de edição |
|||
Linha 1: | Linha 1: | ||
{{Nav2|[[Imagem:C++ Book Cover.png|30px]]|[[../Classes/]]|[[../Encapsulamento/]]}} |
|||
{{reciclagem}} |
|||
{{indentar}} |
|||
{{reciclagem}} |
{{reciclagem}} |
||
Linha 535: | Linha 540: | ||
== Ver também == |
== Ver também == |
||
* [[Programação orientada a objetos/Herança]] - conceito |
* [[Programação orientada a objetos/Herança]] - conceito |
||
{{reciclagem/fim}} |
|||
[[Categoria:Programar em C++|Inherence]] |
[[Categoria:Programar em C++|Inherence]] |
Revisão das 23h30min de 22 de janeiro de 2010
<< Classes | Encapsulamento >> |
Esta página precisa ser reciclada (discuta). Ao melhorá-la, você estará ajudando o Wikilivros. |
Esta página precisa ser reciclada (discuta). Ao melhorá-la, você estará ajudando o Wikilivros. |
Herança
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 : 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:
// Demonstrate inheritance.
#include <iostream>
using namespace std;
class road_vehicle // Define a base class for vehicles.
{
int wheels;
int passengers;
public:
void set_wheels(int num) { wheels = num; }
int get_wheels() { return wheels; }
void set_pass(int num) { passengers = num; }
int get_pass() { return passengers; }
};
class truck : public road_vehicle // Define a truck.
{
int cargo;
public:
void set_cargo(int size) { cargo = size; }
int get_cargo() { return cargo; }
void show();
};
enum type {car, van, wagon};
class automobile : public road_vehicle // Define an automoble.
{
enum type car_type;
public:
void set_type(type t) { car_type = t; }
enum type get_type() { return car_type; }
void show();
};
void truck::show()
{
cout << "wheels: " << get_wheels() << "\n";
cout << "passengers: " << get_pass() << "\n";
cout << "cargo capacity in liters: " << cargo << "\n";
}
void automobile::show()
{
cout << "wheels: " << get_wheels() << "\n";
cout << "passengers: " << get_pass() << "\n";
cout << "type: ";
switch(get_type())
{
case van: cout << "van\n";
break;
case car: cout << "car\n";
break;
case wagon: cout << "wagon\n";
}
}
int main()
{
truck t1, t2;
automobile c;
t1.set_wheels(18);
t1.set_pass(2);
t1.set_cargo(3200);
t2.set_wheels(6);
t2.set_pass(3);
t2.set_cargo(1200);
t1.show();
cout << "\n";
t2.show();
cout << "\n";
c.set_wheels(4);
c.set_pass(6);
c.set_type(van);
c.show();
system ("pause");
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.
base class Access Control.
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.
- Public membrer da base classe:
- 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.
- Private membrer da base classe:
- 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.
- Public membrer da base classe:
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
Herdar múltiplas classes base
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
Constructors e destructors
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 a base class constructors
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; }
Granting Access
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.
Virtual base class
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
- Programação orientada a objetos/Herança - conceito