BCC221
Programação Orientada a Objetos
Prof. Marco Antonio M. Carvalho
2014/2

Site da disciplina:
http://www.decom.ufop.br/marco/

Moodle:
www.decom.ufop.br/moodle

Lista de e-mails:
[email protected]

Para solicitar acesso:
http://groups.google.com/group/bcc221-decom
2
Avisos
3

Prova até o conteúdo desta aula.
4



Classes
Objetos
Métodos










Construtores
Destrutores
Construtores Parametrizados e Vetores de Objetos
Objetos como Parâmetros de Métodos
Métodos que Retornam Objetos
Separando a Interface da Implementação
Composição: Objetos como Membros de Classes
Funções Amigas
Sobrecarga de Operadores
O Ponteiro This
5

Herança
 Compilação
 Redefinição de Métodos
 Construtores e Destrutores em Classes Derivadas



Herança Pública vs. Privada vs. Protegida
Conversões de Tipo entre Base e Derivada
Herança Múltipla
 Classes Base Virtuais
 Compilação
 Construtores em Herança Múltipla
6
7

A herança é uma forma de reuso de software
 O programador cria uma classe que absorve os dados
e o comportamento de uma classe existente;
 Ainda é possível aprimorá-la com novas capacidades.

Além de reduzir o tempo de desenvolvimento, o
reuso de software aumenta a probabilidade de
eficiência de um software
 Componentes já debugados e de qualidade provada
contribuem para isto.
8
Uma classe existente e que será absorvida é chamada
de classe base (ou superclasse);
 A nova classe, que absorve, é chamada de classe
derivada (ou subclasse)

 Que é considerada uma versão especializada da classe
base.

Uma classe base direta é herdada diretamente da
classe derivada
 É possível que haja uma classe base indireta, em que haja
um ou dois níveis de distância em relação à classe
derivada.

A relação entre tais classes define uma hierarquia de
classes.
9
10

A herança define um relacionamento “é um”
 Um carro é um veículo
▪ Todas as propriedades de um veículo são propriedades de um carro.
 Um objeto de uma classe derivada pode ser tratado como
um objeto da classe base.

Os métodos de uma classe derivada podem necessitar
acesso aos métodos e atributos da classe base
 Somente os membros não privados estão disponíveis;
 Ou seja, membros que não devem ser acessíveis através
de herança devem ser privados;
▪ Poderão ser acessíveis por getters e setters públicos, por exemplo.
11

Um possível problema com herança é ter que herdar
atributos ou métodos desnecessários ou
inapropriados
 É responsabilidade do projetista determinar se as
características da classe base são apropriadas para
herança direta e também para futuras classes derivadas.

Ainda, é possível que métodos necessários não se
comportem de maneira especificamente necessária
 Nestes casos, é possível que a classe derivada redefina o
método para que este tenha uma implementação
específica.
12

Vamos criar uma hierarquia de classes para
descrever a comunidade acadêmica:
 A base será descrita por uma classe
CommunityMember;

As primeiras formas de especialização serão
 Empregados (Employee);
 Estudantes (Students);
 Ex-Alunos (Alumnus);

Entre os empregados, temos:
 Os de departamentos ou escolas (Faculty);
 Funcionários de apoio (Staff).
13

Entre os empregados dos departamentos e
escolas (Faculty), temos:
 Professores (Teacher);
 Administradores (Administrator).

Por fim, há uma sutileza
 Os administradores são também professores
▪ Como os chefes de departamento.
14

CommunityMember
 Alumnus;
 Students;
 Employees
▪ Staff;
▪ Faculty
▪ Administrator;
▪ Teacher
 AdministratorTeacher.
Cada seta na hierarquia
apresentada a seguir
representa um
relacionamento “é
um”;
 CommunityMember é a
base direta ou indireta
de todas as classes da
hierarquia.

15
16
•
A cada nível da hierarquia o nível de especialização
dos objetos aumenta
• Objetos mais detalhados.
•
Poderíamos ainda continuar a especializar os objetos
• Elipse, Retângulo e Trapezóide por exemplo.
17

Definimos que a classe TwoDimensionalShape
é derivada da classe Shape da seguinte forma
em C++
class TwoDimensionalShape : public Shape

Este é um exemplo de herança pública
 A forma utilizada mais comumente;
 Note o especificador de acesso;
 Há também a herança privada e protegida.
18

Em todos os tipos de herança, os membros
privados da classe base não são acessíveis a
partir da classe derivada
 Embora sejam herdados e considerados parte da
classe derivada.

Na herança pública, os membros mantêm sua
visibilidade original
 Membros públicos da classe base são membros
públicos da classe derivada;
 Membros protegidos da classe base são membros
protegidos da classe derivada.

Funções amigas não são herdadas.
19

Revisando os especificadores de acesso (ou
visibilidade)
 public: acessível dentro da classe base e em qualquer
parte do programa em que houver uma referência,
ponteiro ou objeto da classe base;
 private: acessível apenas ao código interno da classe
base e a funções amigas.
 protected: acessível ao código interno e funções
amigas da classe base e aos membros e funções
amigas de classes derivadas
▪ Membros de classes derivadas podem chamar os métodos da
classe base, somente invocando o nome.
20

Nosso exemplo de implementação considerará
uma empresa e seus funcionários
 Funcionários Comissionados são pagos com
comissões sobre vendas;
 Funcionários Assalariados Comissionados são pagos
com um salário fixo e também recebem comissões
sobre vendas.

O primeiro tipo de funcionário será
representado pela classe base, enquanto o
segundo tipo será representado pela classe
derivada.
21

A partir deste contexto, serão apresentados 4
exemplos:
Classe ComissionEmployee, que representa funcionários
comissionados
1.
▪
Classe BasePlusCommissionEmployee, que representa
funcionários assalariados comissionados
2.
▪
Sem herança.
Nova classe BasePlusCommissionEmployee
3.
▪
▪
4.
Membros Privados
Usando herança;
Classe ComissionEmployee com membros protected.
Nova herança, porém, classe ComissionEmployee com
membros private.
22
#include <string> // classe string padrão C++
using namespace std;
class CommissionEmployee
{
public:
CommissionEmployee(const string &, const string &, const string &, double = 0.0, double = 0.0);
void setFirstName( const string & ); // configura o nome
string getFirstName() const; // retorna o nome
void setLastName( const string & ); // configura o sobrenome
string getLastName() const; // retorna o sobrenome
void setSocialSecurityNumber( const string & ); // configura o SSN
string getSocialSecurityNumber() const; // retorna o SSN
void setGrossSales( double ); // configura a quantidade de vendas brutas
double getGrossSales() const; // retorna a quantidade de vendas brutas
void setCommissionRate( double ); // configura a taxa de comissão (porcentagem)
double getCommissionRate() const; // retorna a taxa de comissão
double earnings() const; // calcula os rendimentos
void print() const; // imprime o objeto CommissionEmployee
private:
string firstName;
string lastName;
string socialSecurityNumber;
double grossSales; // vendas brutas semanais
double commissionRate; // porcentagem da comissão
};
23

Note os atributos são privados
 Não acessíveis por classes derivadas.

Os getters e setters desta classe são públicos
 Acessíveis por classes derivadas;
 Podem ser utilizados como meio para acessar os atributos
▪ O que é uma boa prática de engenharia de software.

Além disto, getters são declarados como const
 Não podem alterar o valor de nenhum argumento.

As strings recebidas pelo construtor também são
declaradas como referência constante
 Na prática não surte efeito, mas é uma boa prática;
 Passagem por referência e prevenção de alteração.
24
#include <iostream>
using namespace std;
#include "CommissionEmployee.h" // Definição da classe CommissionEmployee
// construtor
CommissionEmployee::CommissionEmployee( const string &first, const string &last,
const string &ssn, double sales, double rate)
{
firstName = first; // deve validar
lastName = last; // deve validar
socialSecurityNumber = ssn; // deve validar
setGrossSales( sales ); // valida e armazena as vendas brutas
setCommissionRate( rate ); // valida e armazena a taxa de comissão
}
// configura o nome
void CommissionEmployee::setFirstName( const string &first )
{
firstName = first; // deve validar
}
// retorna o nome
string CommissionEmployee::getFirstName() const
{
return firstName;
}
25

Note que o construtor da classe base acessa e
realiza atribuições aos atributos diretamente
 Os atributos sales e rate são acessados através de
métodos porque estes realizam testes de
consistência nos valores passados por
parâmetros;
 Os demais atributos também poderiam ser
checados quanto a sua consistência
▪ Número correto de digitos e comprimento máximo de
strings.
26
// configura o sobrenome
void CommissionEmployee::setLastName( const string &last )
{
lastName = last; // deve validar
}
// retorna o sobrenome
string CommissionEmployee::getLastName() const
{
return lastName;
}
// configura o SSN
void CommissionEmployee::setSocialSecurityNumber( const string &ssn )
{
socialSecurityNumber = ssn; // deve validar
}
// retorna o SSN
string CommissionEmployee::getSocialSecurityNumber() const
{
return socialSecurityNumber;
}
27
// configura a quantidade de vendas brutas
void CommissionEmployee::setGrossSales( double sales )
{
grossSales = ( sales < 0.0 ) ? 0.0 : sales;
}
// retorna a quantidade de vendas brutas
double CommissionEmployee::getGrossSales() const
{
return grossSales;
}
// configura a taxa de comissão
void CommissionEmployee::setCommissionRate( double rate )
{
commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0;
}
// retorna a taxa de comissão
double CommissionEmployee::getCommissionRate() const
{
return commissionRate;
}
28
// calcula os rendimentos
double CommissionEmployee::earnings() const
{
return commissionRate * grossSales;
}
// imprime o objeto CommissionEmployee
void CommissionEmployee::print() const
{
cout << "commission employee: " << firstName << ' ' << lastName
<< "\nsocial security number: " << socialSecurityNumber
<< "\ngross sales: " << grossSales
<< "\ncommission rate: " << commissionRate;
}
29
#include <iostream>
#include <iomanip>
using namespace std;
#include "CommissionEmployee.h" // Definição da classe CommissionEmployee
int main()
{
// instancia um objeto CommissionEmployee
CommissionEmployee employee("Sue", "Jones", "222-22-2222", 10000, .06 );
// configura a formatação de saída de ponto flutuante
cout << fixed << setprecision( 2 );
// obtém os dados do empregado comissionado
cout << "Employee information obtained by get functions: \n"
<< "\nFirst name is " << employee.getFirstName()
<< "\nLast name is " << employee.getLastName()
<< "\nSocial security number is "
<< employee.getSocialSecurityNumber()
<< "\nGross sales is " << employee.getGrossSales()
<< "\nCommission rate is " << employee.getCommissionRate() << endl;
30
employee.setGrossSales( 8000 ); // configura vendas brutas
employee.setCommissionRate( .1 ); // configura a taxa de comissão
cout << "\nUpdated employee information output by print function: \n"<< endl;
employee.print(); // exibe as novas informações do empregado
// exibe os rendimentos do empregado
cout << "\n\nEmployee's earnings: $" << employee.earnings() << endl;
}
return 0;
31
Employee information obtained by get functions:
First name is Sue
Last name is Jones
Social security number is 222-22-2222
Gross sales is 10000.00
Commission rate is 0.06
Updated employee information output by print function:
commission employee: Sue Jones
social security number: 222-22-2222
gross sales: 8000.00
commission rate: 0.10
Employee's earnings: $800.00
32

Vamos agora criar a classe
BasePlusCommissionEmployee, que
representa funcionários assalariados
comissionados
 Embora seja uma especialização da classe
anterior, vamos definir completamente a classe.
33
#include <string> // classe string padrão C++
using namespace std;
class BasePlusCommissionEmployee
{
public:
BasePlusCommissionEmployee( const string &, const string &, const string &,
double = 0.0, double = 0.0, double = 0.0 );
void setFirstName( const string & ); // configura o nome
string getFirstName() const; // retorna o nome
void setLastName( const string & ); // configura o sobrenome
string getLastName() const; // retorna o sobrenome
void setSocialSecurityNumber( const string & ); // configura o SSN
string getSocialSecurityNumber() const; // retorna o SSN
void setGrossSales( double ); // configura a quantidade de vendas brutas
double getGrossSales() const; // retorna a quantidade de vendas brutas
void setCommissionRate( double ); // configura a taxa de comissão
double getCommissionRate() const; // retorna a taxa de comissão
void setBaseSalary( double ); // configura o salário-base
double getBaseSalary() const; // retorna o salário-base
double earnings() const; // calcula os rendimentos
void print() const; // imprime o objeto BasePlusCommissionEmployee
34
private:
string firstName;
string lastName;
string socialSecurityNumber;
double grossSales; // vendas brutas semanais
double commissionRate; // porcentagem da comissão
double baseSalary; // salário-base
};
35


Note que foram adicionados um atributo com
respectivo getter e setter, além de um
parâmetro adicional no construtor;
O restante do código é basicamente
redundante em relação à classe
ComissionEmployee.
36
#include <iostream>
using namespace std;
// Definição da classe BasePlusCommissionEmployee
#include "BasePlusCommissionEmployee.h"
// construtor
BasePlusCommissionEmployee::BasePlusCommissionEmployee(
const string &first, const string &last, const string &ssn,
double sales, double rate, double salary )
{
firstName = first; // deve validar
lastName = last; // deve validar
socialSecurityNumber = ssn; // deve validar
setGrossSales( sales ); // valida e armazena as vendas brutas
setCommissionRate( rate ); // valida e armazena a taxa de comissão
setBaseSalary( salary ); // valida e armazena salário-base
}
// configura o nome
void BasePlusCommissionEmployee::setFirstName( const string &first )
{
firstName = first; // deve validar
}
37
// retorna o nome
string BasePlusCommissionEmployee::getFirstName() const
{
return firstName;
}
// configura o sobrenome
void BasePlusCommissionEmployee::setLastName(const string &last)
{
lastName = last; // deve validar
}
// retorna o sobrenome
string BasePlusCommissionEmployee::getLastName() const
{
return lastName;
}
// configura o SSN
void BasePlusCommissionEmployee::setSocialSecurityNumber(const string &ssn)
{
socialSecurityNumber = ssn; // deve validar
}
38
// retorna o SSN
string BasePlusCommissionEmployee::getSocialSecurityNumber() const
{
return socialSecurityNumber;
}
// configura a quantidade de vendas brutas
void BasePlusCommissionEmployee::setGrossSales( double sales )
{
grossSales = ( sales < 0.0 ) ? 0.0 : sales;
}
// retorna a quantidade de vendas brutas
double BasePlusCommissionEmployee::getGrossSales() const
{
return grossSales;
}
// configura a taxa de comissão
void BasePlusCommissionEmployee::setCommissionRate( double rate )
{
commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0;
}
39
// retorna a taxa de comissão
double BasePlusCommissionEmployee::getCommissionRate() const
{
return commissionRate;
}
// configura o salário-base
void BasePlusCommissionEmployee::setBaseSalary( double salary )
{
baseSalary = ( salary < 0.0 ) ? 0.0 : salary;
}
// retorna o salário-base
double BasePlusCommissionEmployee::getBaseSalary() const
{
return baseSalary;
}
// calcula os rendimentos
double BasePlusCommissionEmployee::earnings() const
{
return baseSalary + ( commissionRate * grossSales );
}
40
// imprime o objeto BasePlusCommissionEmployee
void BasePlusCommissionEmployee::print() const
{
cout << "base-salaried commission employee: " << firstName << ' '
<< lastName << "\nsocial security number: " << socialSecurityNumber
<< "\ngross sales: " << grossSales
<< "\ncommission rate: " << commissionRate
<< "\nbase salary: " << baseSalary;
}
41
#include <iostream>
#include <iomanip>
using namespace std;
#include "BasePlusCommissionEmployee.h"
int main()
{
// instancia o objeto BasePlusCommissionEmployee
BasePlusCommissionEmployee
employee( "Bob", "Lewis", "333-33-3333", 5000, .04, 300 );
// configura a formatação de saída de ponto flutuante
cout << fixed << setprecision( 2 );
// obtém os dados do empregado comissionado
cout << "Employee information obtained by get functions: \n"
<< "\nFirst name is " << employee.getFirstName()
<< "\nLast name is " << employee.getLastName()
<< "\nSocial security number is "
<< employee.getSocialSecurityNumber()
<< "\nGross sales is " << employee.getGrossSales()
<< "\nCommission rate is " << employee.getCommissionRate()
<< "\nBase salary is " << employee.getBaseSalary() << endl;
42
employee.setBaseSalary( 1000 ); // configura o salário-base
cout << "\nUpdated employee information output by print function: \n"
<< endl;
employee.print(); // exibe as novas informações do empregado
// exibe os rendimentos do empregado
cout << "\n\nEmployee's earnings: $" << employee.earnings() << endl;
}
return 0;
43
Employee information obtained by get functions:
First name is Bob
Last name is Lewis
Social security number is 333-33-3333
Gross sales is 5000.00
Commission rate is 0.04
Base salary is 300.00
Updated employee information output by print function:
base-salaried commission employee: Bob Lewis
social security number: 333-33-3333
gross sales: 5000.00
commission rate: 0.04
base salary: 1000.00
Employee's earnings: $1200.00
44
45

Como pode ser notado:
 Ambas as classes compartilham a maior parte dos
atributos, que são privados;
 Consequentemente, os getters e setters relacionados
também são compartilhados
▪ Foi acrescentado apenas um getter e um setter devido ao novo
atributo.
 Parte dos métodos foi adaptada para tratar o atributo
extra na segunda classe
▪ Construtor e print.
 Literalmente o código original foi copiado e adaptado.

Quando a redundância entre classes acontece,
caracteriza-se a necessidade de herança.
46

A replicação de código pode resultar em replicação de
erros:
 A manutenção é dificultada, pois cada cópia tem que ser
corrigida;
 Desperdício de tempo.

Imagine um sistema composto de várias classes
parecidas divididas em diversos códigos replicados
 Menos é mais.

Definimos uma classe que absorverá os atributos e
métodos redundantes
 A classe base;
 A manutenção dada na classe base se reflete nas classes
derivadas automaticamente.
47

Nosso próximo exemplo fixa a classe
ComissionEmployee como classe base
 Note que utilizaremos a mesma definição desta classe do
exemplo anterior;
 Os atributos continuam privados.

A classe BasePlusCommissionEmployee será a classe
derivada
 Acrescentará o atributo baseSalary;
 Acrescentará também getter e setter e redefinirá dois
métodos.

Qualquer tentativa de acesso aos membros privados
da classe base gerará erro de compilação.
48
#include <string> // classe string padrão C++
using namespace std;
#include "CommissionEmployee.h" // Declaração da classe CommissionEmployee
class BasePlusCommissionEmployee : public CommissionEmployee
{
public:
BasePlusCommissionEmployee( const string &, const string &, const string &,
double = 0.0, double = 0.0, double = 0.0 );
void setBaseSalary( double ); // configura o salário-base
double getBaseSalary() const; // retorna o salário-base
double earnings() const; // calcula os rendimentos
void print() const; // imprime o objeto BasePlusCommissionEmployee
private:
double baseSalary; // salário-base
};
49


O operador : define a herança;
Note que a herança é pública
 Todos os métodos públicos da classe base são também
métodos públicos da classe derivada
▪ Embora não os vejamos na definição da classe derivada, eles fazem
parte dela.
 Note que o construtor não foi herdado
▪ Foi definido um construtor específico.

É necessário incluir o header da classe a ser herdada
 Permite a utilização do nome da classe, determina o
tamanho dos objetos e garante que a interface será
respeitada.
50
#include <iostream>
using namespace std;
// Definição da classe BasePlusCommissionEmployee
#include "BasePlusCommissionEmployee.h"
// construtor
BasePlusCommissionEmployee::BasePlusCommissionEmployee(const string &first,
const string &last, const string &ssn, double sales, double rate, double salary)
// chama explicitamente o construtor da classe básica
: CommissionEmployee( first, last, ssn, sales, rate )
{
setBaseSalary( salary ); // valida e armazena salário-base
}
// configura o salário-base
void BasePlusCommissionEmployee::setBaseSalary( double salary )
{
baseSalary = ( salary < 0.0 ) ? 0.0 : salary;
}
// retorna o salário-base
double BasePlusCommissionEmployee::getBaseSalary() const
{
return baseSalary;
}
51
// calcula os rendimentos
double BasePlusCommissionEmployee::earnings() const
{
// a classe derivada não pode...
return baseSalary + ( commissionRate * grossSales );
}
// imprime o objeto BasePlusCommissionEmployee
void BasePlusCommissionEmployee::print() const
{
// a classe derivada não pode...
cout << "base-salaried commission employee: " << firstName << ' '
<< lastName << "\nsocial security number: " << socialSecurityNumber
<< "\ngross sales: " << grossSales
<< "\ncommission rate: " << commissionRate
<< "\nbase salary: " << baseSalary;
}
52

Neste exemplo, o construtor da classe derivada
chama explicitamente o construtor da classe
base
 Sintaxe inicializadora da classe base;
 É necessário que a classe derivada tenha um
construtor para que o construtor da classe base seja
chamado;
 Se o construtor da classe base não for chamado
explicitamente, o compilador chamará
implicitamente o construtor default (sem
argumentos) da classe base
▪ Se este não existir, ocorrerá um erro de compilação.
53

Os métodos earnings e print() deste exemplo
gerarão erros de compilação
 Ambos tentam acessar diretamente membros
privados da classe base, o que não é permitido
▪ Mesmo para classes intimamente relacionadas.
 Poderíamos utilizar os getters associados a tais
atributos para evitar os erros de compilação
▪ Uma vez que os getters são públicos.
 Os métodos podem ser redefinidos sem causar
erros de compilação, como veremos em breve.
54
55

Vamos modificar o primeiro exemplo (classe
ComissionEmployee) para que seus atributos
sejam protected
 O modificador de acesso protected (# em UML)
permite que um membro seja acessível por:
▪ Membros e funções amigas da classe base;
▪ Membros e funções amigas das classes derivadas.
 Desta forma o segundo exemplo (classe
BasePlusComissionEmployee) compilará sem erros
▪ Poderá acessar os atributos diretamente.
56
#include <string> // classe string padrão C++
using namespace std;
class CommissionEmployee
{
public:
CommissionEmployee( const string &, const string &, const string &,
double = 0.0, double = 0.0 );
void setFirstName( const string & ); // configura o nome
string getFirstName() const; // retorna o nome
void setLastName( const string & ); // configura o sobrenome
string getLastName() const; // retorna o sobrenome
void setSocialSecurityNumber( const string & ); // configura SSN
string getSocialSecurityNumber() const; // retorna SSN
void setGrossSales( double ); // configura a quantidade de vendas brutas
double getGrossSales() const; // retorna a quantidade de vendas brutas
57
void setCommissionRate( double ); // configura a taxa de comissão
double getCommissionRate() const; // retorna a taxa de comissão
double earnings() const; // calcula os rendimentos
void print() const; // imprime o objeto CommissionEmployee
protected:
string firstName;
string lastName;
string socialSecurityNumber;
double grossSales; // vendas brutas semanais
double commissionRate; // porcentagem da comissão
};
58

A implementação da classe
ComissionEmployee não muda em
rigorosamente nada
 Internamente à classe base, nada muda se um
membro é privado ou protegido.

A implementação da classe
BasePlusComissionEmployee também não
muda
 Na verdade, apenas herdando da nova classe base
é que ela funciona!
59
CommissionEmployee
-firstName
-lastName
-socialSecurityNumber
-grossSales
-comissionRate
+commissionEmployee()
+setFirstName()
+getFirstName()
+setLastName()
+getLastName()
+setSocialSecurityNumber()
+getSocialSecurityNumber()
+setGrossSales()
+getGrossSales()
+setCommissionRate()
+getCommissionRate()
+earnings()
+print()
BasePlusCommissionEmployee
-baseSalary
+setBaseSalary()
+getBaseSalary()
+earnings()
+print()
60

Utilizar atributos protegidos nos dá uma leve melhoria
de desempenho
 Não é necessário ficar chamando funções (getters e
setters).

Por outro lado, também nos gera dois problemas
essenciais:
 Uma vez que não se usa getter e setter, pode haver
inconsistência nos valores do atributo
▪ Nada garante que o programador que herdar fará uma validação.
 Qualquer alteração de nomenclatura de um atributo
invalida toda a classe que herda seu código
▪ Uma classe derivada deve depender do serviço prestado, e não da
implementação da classe base.
61

Quando devemos usar protected então?
 Quando nossa classe base precisar fornecer um
serviço (método) apenas para classes derivadas e
funções amigas, ninguém mais.


Declarar membros como privados permite
que a implementação da classe base seja
alterada sem implicar em alteração da
implementação da classe derivada;
O padrão mais utilizado é atributos privados
e getters e setters públicos.
62

Vamos adequar nossas classes dos exemplos
anteriores ao padrão proposto
 De acordo com as boas práticas de engenharia de
software.

A classe base volta a ter atributos privados
 A implementação também passa a utilizar getters e
setters internamente.

A classe derivada não acessa os atributos
diretamente
 Utiliza getters e setters;
 Somente a implementação da classe é alterada.
63
#include <string> // classe string padrão C++
using namespace std;
class CommissionEmployee
{
public:
CommissionEmployee(const string &, const string &, const string &, double = 0.0, double = 0.0);
void setFirstName( const string & ); // configura o nome
string getFirstName() const; // retorna o nome
void setLastName( const string & ); // configura o sobrenome
string getLastName() const; // retorna o sobrenome
void setSocialSecurityNumber( const string & ); // configura o SSN
string getSocialSecurityNumber() const; // retorna o SSN
void setGrossSales( double ); // configura a quantidade de vendas brutas
double getGrossSales() const; // retorna a quantidade de vendas brutas
void setCommissionRate( double ); // configura a taxa de comissão (porcentagem)
double getCommissionRate() const; // retorna a taxa de comissão
double earnings() const; // calcula os rendimentos
void print() const; // imprime o objeto CommissionEmployee
private:
string firstName;
string lastName;
string socialSecurityNumber;
double grossSales; // vendas brutas semanais
double commissionRate; // porcentagem da comissão
};
64
CommissionEmployee::CommissionEmployee(const string &first, const string
&last, const string &ssn, double sales, double rate )
: firstName( first ), lastName( last ), socialSecurityNumber( ssn )
{
setGrossSales( sales ); // valida e armazena as vendas brutas
setCommissionRate( rate ); // valida e armazena a taxa de comissão
}
double CommissionEmployee::earnings() const
{
return getCommissionRate() * getGrossSales();
}
// imprime o objeto CommissionEmployee
void CommissionEmployee::print() const
{
cout << "commission employee: "
<< getFirstName() << ' ' << getLastName()
<< "\nsocial security number: " << getSocialSecurityNumber()
<< "\ngross sales: " << getGrossSales()
<< "\ncommission rate: " << getCommissionRate();
}
65

Note que o construtor inicializa os atributos
em seu próprio cabeçalho
 Após a assinatura, utilizamos : e em seguida o
nome do parâmetro seguido do valor a ser
atribuído, entre parênteses;
 Forma alternativa para o construtor;
 Executado antes do próprio construtor;
 Em alguns casos, é obrigatório
▪ Constantes e referências.
66
// calcula os rendimentos
double BasePlusCommissionEmployee::earnings() const
{
return getBaseSalary() + CommissionEmployee::earnings();
}
// imprime o objeto BasePlusCommissionEmployee
void BasePlusCommissionEmployee::print() const
{
cout << "base-salaried ";
// invoca a função print de CommissionEmployee
CommissionEmployee::print();
}
cout << "\nbase salary: " << getBaseSalary();
67
Employee information obtained by get functions:
First name is Bob
Last name is Lewis
Social security number is 333-33-3333
Gross sales is 5000.00
Commission rate is 0.04
Base salary is 300.00
Updated employee information output by print function:
base-salaried commission employee: Bob Lewis
social security number: 333-33-3333
gross sales: 5000.00
commission rate: 0.04
base salary: 1000.00
Employee's earnings: $1200.00
68
69

Compilamos primeiro a classe base
 g++ -c ComissionEmployee.cpp

Compilamos então a classe derivada
 g++ -c BasePlusComissionEmployee.cpp

Compilamos o programa driver utilizando os arquivos
.o das duas compilações anteriores
 g++ ComissionEmployee.o BasePlusComissionEmployee.o
driver.cpp –o programa

Uma outra maneira é compilar todos os arquivos .cpp
das classes
 g++ -c *Employee.cpp
 g++ *.o driver.cpp –o programa
70
Continua na
próxima aula...
71
72

Classes derivadas podem redefinir métodos da classe
base
 A assinatura pode até mudar, embora o nome do método
permaneça;
 A precedência é do método redefinido na classe derivada
▪ Na verdade, este substitui o método da classe base na classe
derivada;
▪ Em caso de assinaturas diferentes, pode haver erro de compilação
caso tentemos chamar o método da classe base.

A classe derivada pode chamar o método da classe
base
 Desde que use o operador de escopo :: precedido pelo
nome da classe base.
73

É comum que métodos redefinidos chamem
o método original dentro de sua redefinição e
acrescentem funcionalidades
 Como no exemplo anterior, em que frases
adicionais são impressas na redefinição do
método print().
74
75

Como vimos no exemplo, instanciar uma classe
derivada inicia uma cadeia de chamadas a
construtores
 O construtor da classe derivada chama o construtor da
classe base antes de executar suas próprias ações;
 O construtor da classe base é chamada direta ou
indiretamente (construtor default).

Considerando um hierarquia de classes mais extensa:
 O primeiro construtor chamado é a última classe derivada
▪ E é o último a ser executado.
 O último construtor chamado é o da classe base
▪ E é o primeiro a ser executado.
76
Primeiro a ser
executado
Classe
Base
Segundo a ser
executado
Terceiro a ser
executado
Derivada1
Chama o construtor
da base direta
Derivada2
Chama o construtor
da base direta
Chama o construtor
da base direta
Último a ser
executado
Derivada3
Instância
Chama o construtor
da classe
77

O contrário acontece em relação aos destrutores
 A execução começa pela classe derivada e é
propagada até a classe base.

Considerando um hierarquia de classes mais
extensa:
 O primeiro destrutor chamado é a última classe
derivada
▪ E é também o primeiro a ser executado.
 O último construtor chamado é o da classe base
▪ E é também o último a ser executado.
78
Último destrutor
a ser chamado
Classe
Base
Derivada1
Executa e depois chama
o destrutor da base direta
Chama o destrutor
da classe
Derivada2
Executa e depois chama
o destrutor da base direta
Derivada3
Instância
Executa e depois chama
o destrutor da base direta
79

Caso um atributo de uma classe derivada seja
um objeto de outra classe:
 Seu construtor será o primeiro a ser executado;
 Seu destrutor será o último a ser executado.

Atenção!
 Construtores e destrutores de uma classe base
não são herdados;
 No entanto, podem ser chamados pelos
construtores e destrutores das classes derivadas.
80
81

Herança Pública
 Os membros públicos da classe base serão membros públicos da classe
derivada
▪ Poderão ser acessados por objetos da classe derivada.
 Os membros protegidos da classe base serão membros protegidos da
classe derivada;

Herança Privada
 Tantos os membros públicos quanto os protegidos da classe base serão
membros privados da classe derivada;
 Acessíveis aos membros da classe derivada, mas não aos seus objetos.

Herança Protegida
 Tantos os membros públicos quanto os protegidos da classe base serão
membros protegidos da classe derivada
▪ Disponíveis aos membros da classe derivada e as que herdarem dela;
▪ Um objeto da classe derivada não terá acesso a nenhum membro da classe
derivada.
82
class Base
{
protected:
int protegido;
private:
int privado;
public:
int publico;
};
83
class Derivada1: public Base
{
private:
int a, b, c;
public:
Derivada1()
{
a = protegido;
b = privado;//ERRO: Não acessível
c = publico;
}
};
84
class Derivada2: private Base
{
private:
int a, b, c;
public:
Derivada2()
{
a = protegido;
b = privado;//ERRO: Não acessível
c = publico;
}
};
85
class Derivada3: protected Base
{
private:
int a, b, c;
public:
Derivada3()
{
a = protegido;
b = privado;//ERRO: Não acessível
c = publico;
}
};
86
int main()
{
int x;
Derivada1 HPublica;
x = HPublica.protegido;
x = HPublica.privado;
x = HPublica.publico;
//ERRO: Não acessível
//ERRO: Não acessível
//OK
Derivada2 HPrivada;
x = HPrivada.protegido;
x = HPrivada.privado;
x = HPrivada.publico;
//ERRO: Não acessível
//ERRO: Não acessível
//ERRO: Não acessível
Derivada3 HProtegida;
x = HProtegida.protegido;
x = HProtegida.privado;
x = HProtegida.publico;
}
//ERRO: Não acessível
//ERRO: Não acessível
//ERRO: Não acessível
return 0;
87
88
89

Se necessário, podemos converter um objeto de uma
classe derivada em um objeto da classe base
 A classe base pode conter menos atributos que a classe
derivada
▪ Os atributos em comum são copiados;
▪ Os atributos extras são desperdiçados.

O contrário não pode ser feito
 Conversão de um objeto da classe base para um objeto da
classe derivada.
A conversão de tipos está intimamente relacionada com o
Polimorfismo, que veremos em breve.
 Para nosso exemplo, consideremos duas classes:

 Base e Derivada;
 Em que a segunda herda da primeira.
90
int main()
{
Base obj1;
Derivada obj2;
}
obj1.get();
obj2.get();
//”Preenche” o objeto.
//”Preenche” o objeto.
obj1 = obj2;
obj2 = obj1;
//OK.
//ERRO! Não pode ser convertido.
return 0;
91
92

Uma classe pode ser derivada a partir de mais
que uma classe base
 Caracterizando assim a herança múltipla.

A complexidade relacionada à herança múltipla
diz respeito ao projeto do relacionamento das
classes
 A sintaxe é similar à herança simples;
 Após o cabeçalho da classe derivada, usamos : e
listamos as classes a serem herdadas, separadas por
vírgula e com modificadores de acesso individuais.
class Derivada: public Base1, public Base2
93

Considere para nosso exemplo que queremos modelar o
registro de imóveis de uma imobiliária que trabalha
apenas com vendas
 Já possuímos uma classe Cadastro que utilizamos para outros
fins;
 Já possuímos também uma classe Imóvel, que armazena os
dados de um imóvel;
 Decidimos então não modificar as classes, e criar a classe
Venda
▪ Vamos incorporar o cadastro do dono e do imóvel a partir das nossas
classes já existentes.
 Resolvemos criar uma classe Tipo, que determina se o imóvel é
residencial, comercial ou galpão.
 Como último detalhe, os dados sobre comprador não são
interessantes para a classe Venda.
94
#include<iostream>
using namespace std;
#include<string>
using namespace std;
class Cadastro
{
private:
string nome, fone;
public:
void setNome();
void setFone();
void print();
};
#include "Cadastro.h"
void Cadastro::setNome()
{
cout<<"Nome: "<<endl;
getline(cin, nome);
}
void Cadastro::setFone()
{
cout<<"Telefone: "<<endl;
getline(cin, fone);
}
void Cadastro::print()
{
cout<<"Nome: "<<nome<<endl
<<"Telefone:”<<fone
<<endl;
}
95
#include<string>
using namespace std;
class Imovel
{
private:
string endereco, bairro;
float areaUtil, areaTotal;
int quartos;
public:
void setEndereco();
void setBairro();
void setAreaUtil();
void setAreaTotal();
void setQuartos();
void print();
};
96
#include<iostream>
using namespace std;
#include "Imovel.h"
void Imovel::setEndereco()
{
cout<<"Endereço:"<<endl;
getline(cin, endereco);
}
void Imovel::setBairro()
{
cout<<"Bairro:"<<endl;
getline(cin, bairro);
}
void Imovel::setAreaUtil()
{
cout<<"Área Útil:"<<endl;
cin>>areaUtil;
}
void Imovel::setAreaTotal()
{
cout<<"Área Total:"<<endl;
cin>>areaTotal;
}
void Imovel::setQuartos()
{
cout<<"Número de Quartos:“
<<endl;
cin>>quartos;
}
void Imovel::print()
{
cout <<"Endereço: “
<<endereco<<endl
<<"Bairro: "<<bairro
<<endl<<"Área Útil:”
<<areaUtil<<endl
<<"Área Total:“
<<areaTotal<<endl
<<"Quartos: "<<quartos
<<endl;
}
97
#include<iostream>
using namespace std;
#include<string>
using namespace std;
class Tipo
{
private:
string tipoImovel;
public:
void setTipoImovel();
void print();
};
#include "Tipo.h"
void Tipo::setTipoImovel()
{
cout<<"Tipo do Imóvel:"<<endl;
getline(cin, tipoImovel);
cin.ignore(10, '\n');
}
void Tipo::print()
{
cout<<"Tipo do Imóvel: “
<<tipoImovel<<endl;
}
98
#include<iostream>
using namespace std;
#include"Cadastro.h"
#include"Imovel.h"
#include"Tipo.h"
class Venda: private Cadastro, Imovel, Tipo
{
private:
float valor;
public:
void set();
void print();
};
99

A classe Venda realiza herança privada
 Os métodos da classe base não são acessíveis aos
objetos.

Atenção!
 Herdando da classe Cadastro, a classe Venda só terá
espaço para os dados de um único cadastro;
 Se quiséssemos cadastrar o comprador, não seria
possível
▪ Um objeto da classe Cadastro resolveria este “problema”.
 Não é possível herdar duas vezes da mesma classe.
100
#include<iostream>
using namespace std;
#include"Venda.h"
void Venda::set()
{
cout<<"Proprietário:"<<endl;
Cadastro::setNome();
Cadastro::setFone();
cout<<"Imóvel:"<<endl;
Imovel::setEndereco();
Imovel::setBairro();
Imovel::setAreaTotal();
Imovel::setAreaUtil();
Tipo::setTipoImovel();
}
void Venda::print()
{
cout<<"Proprietário:"<<endl;
Cadastro::print();
cout<<"Imóvel:"<<endl;
Imovel::print();
Tipo::print();
cout<<"Valor: $"<<valor<<endl;
}
cout<<"Valor de Venda:"<<endl;
cin>>valor;
cin.ignore(10, '\n');
101
Imovel
Cadastro
-nome : char
-fone : char
+setNome()
+setFone()
+print()
-endereco : char
-bairro : char
-aeraUtil : float
-areaTotal : float
-quartos : double
+setEndereco()
+setBairro()
+setAreaUtil()
+setAreaTotal()
+setQuartos()
+print()
«signal»-()
Tipo
-tipoImovel : char
+setImovel()
+print()
Venda
-valor : float
+set()
+print()
102

Note que todas as classes base possuem um
método print()
 Dentro da classe derivada, devemos informar ao
compilador a qual classe estamos nos referindo;
 Utilizamos o nome da classe, o operador de escopo e
o nome do método para que fique claro.
Base::metodo();

No caso de herança pública, o objeto que chame
o método também deve usar o nome da classe e
o operador de escopo
obj.Base::metodo();
103
#include<iostream>
using namespace std;
#include"Venda.h"
int main()
{
Venda galpao;
}
galpao.set();
galpao.print();
104
Proprietário:
Nome: marco
Telefone: 3552-1663
Imóvel:
Endereço: Campus Morro do Cruzeiro
Bairro: Bauxita
Área Útil: 8
Área Total: 8
Quartos: 0
Tipo do Imóvel: Sala
Valor: R$0
105
106

Compilamos primeiro cada classe base
 g++ -c Cadastro.cpp
 g++ -c Imovel.cpp
 g++ -c Tipo.cpp

Compilamos então a classe derivada
 g++ -c Venda.cpp

Compilamos o programa driver utilizando os arquivos .o
das duas compilações anteriores
 g++ Cadastro.o Imovel.o Tipo.o Venda.o driver.cpp -o programa

Uma outra maneira é compilar todos os arquivos .cpp das
classes
 g++ -c *.cpp
 g++ *.o driver.cpp -o programa
107
108

A herança múltipla fornece facilidades para reuso de
software
 O poder de combinar classes.

No entanto, seu uso indiscriminado pode gerar
problemas em uma estrutura de herança
 Por exemplo, o problema do diamante, em que uma classe
derivada herda de uma classe base indireta mais de uma
vez.
109
Veiculo
VeiculoTerrestre
Automóvel

VeiculoAquatico
VeiculoAnfibio
Barco
Neste exemplo, a classe VeiculoAnfibio herda
indiretamente duas vezes da classe Veiculo
 Uma através da classe VeiculoTerrestre;
 Uma através da classe VeiculoAquatico.
110

Considerando o exemplo anterior, suponhamos que
desejamos utilizar um objeto da classe
VeiculoAnfibio para invocar um método da classe
Veiculo
 Qual seria o caminho para atingir a classe Veiculo?
▪ Qual dos membros seria utilizado?
 Haveria ambiguidade, portanto, o código não compilaria.
111

O problema de subobjetos duplicados pode ser
resolvido com herança de classe base virtual
 Quando uma classe base é definida como virtual, apenas
um subobjeto aparece na classe derivada.
112

A única alteração necessária em um código
com o problema do diamante é a definição de
uma classe base virtual
 Basta adicionar a palavra virtual antes do nome
da classe na especificação da herança;
 A adição deve ser feita um nível antes da herança
múltipla
▪ Porém, o efeito ocorrerá quando houver herança
múltipla.
113
Veiculo
VeiculoTerrestre
Automóvel
VeiculoAquatico
VeiculoAnfibio
Barco
114

Os cabeçalhos das classes
VeiculoTerrestre e VeiculoAquatico
passam de
class VeiculoTerrestre: public class Veiculo
class VeiculoAquatico: public class Veiculo

para
class VeiculoTerrestre: virtual public class Veiculo
class VeiculoAquatico: virtual public class Veiculo
115
116

No exemplo anterior, as classes base não
possuiam construtores
 Por motivos de simplicidade da listagem dos códigos.

No entanto, caso tivessem, a sintaxe é parecida
com a utilizada para herança simples
 O construtor de cada classe base é chamado
explicitamente
▪ Sintaxe inicializadora da classe base.
 Separados por vírgula, e enviando os parâmetros
necessários.
117
É necessário que a classe derivada tenha um
construtor para que os construtores das classes
base sejam chamados;
 Se o construtor de uma classe base não for
chamado explicitamente, o compilador chamará
implicitamente o construtor default (sem
argumentos) de tal classe

 Se este não existir, ocorrerá um erro de compilação.

Vamos supor que nosso exemplo anterior possui o
construtor default para cada classe base e para a
classe derivada.
118
Venda::Venda(): Cadastro(), Imovel(), Tipo(), valor(0.0)
{
//construtor default
}
Venda::Venda(string n, string f, string e, string b, float au, float at, int q,
string t, float v):
Cadastro(n, f), Imovel(e, b, au, at, q), Tipo(t), valor(v)
{
}
//construtor parametrizado
119
Perguntas?
120



Arquivos
Prova
Polimorfismo
 Funções Virtuais
 Resolução Dinâmica
 Classes Abstratas
▪ Funções Virtuais Puras
 Conversão de Tipos
 Destrutores Virtuais
121
FIM
122
Download

C++: Herança - DECOM-UFOP