-
Avanado
Orientao a Objectos
-
entao a Objectos
-
ndice Introduo a classes Private e public Mtodos private
Construtores e destrutores Mtodos const Interface e implementao
Objetos como membros Classes internas new e delete com objetos
Acessando membros via ponteiro Ponteiros como membros de uma classe
O ponteiro this Referncias a objetos Funes membro sobrecarregadas
Funes membro com valores default Sobrecarregando construtores
Inicializando variveis membro Construtor de cpia Mtodos virtuais e
passagem por valor Construtor de cpia virtual Sobrecarregando o
operador ++ Sobrecarregando o operador + Sobrecarregando o operador
= Converso entre objetos e tipos simples Arrays de objetos Uma
classe string Exemplo de lista encadeada Introduo a herana Ordem de
chamada a construtores Argumentos para construtores da classe base
Superposio de mtodos Ocultando mtodos da classe base Acessando
mtodos superpostos da classe base Mtodos virtuais Chamando mltiplas
funes virtuais
- Introduo a classes Teoria No Curso C++ Bsico, aprendemos sobre
diversos tipos de variveis, como int, long e char. O tipo da
varivel diz muito sobre ela. Por exemplo, se declararmos x e y como
sendo unsigned int, sabemos que cada uma delas pode armazenar
apenas valores positivos ou zero, dentro de uma faixa bem definida
de valores. esse o significado de dizer que uma varivel unsigned
int: tentar colocar um valor de outro tipo causa um erro de
compilao. Assim, a declarao do tipo de uma varivel indica: (a) O
tamanho da varivel na memria (b) Que tipo de informao a varivel
pode conter (c) Que operaes podem ser executadas com ela Mais
genericamente, um tipo uma categoria. No mundo real, temos tipos
familiares como carro, casa, pessoa, fruta e forma. Em C++, um
programador pode criar qualquer tipo de que precise, e cada novo
tipo pode ter funcionalidade similar dos tipos embutidos na
linguagem. A construo class (classe) define as caractersticas de um
novo tipo de objeto, criado pelo programador. Exemplo //
InClass.cpp // Ilustra o uso // de uma classe simples. #include //
Define uma classe. class Cliente { public: int numCliente; float
saldo; }; // Fim de class Cliente. int main() { // Cria um objeto
// da classe cliente. Cliente objCliente; // Atribui valores s //
variveis do objeto // cliente. objCliente.numCliente = 25;
objCliente.saldo = 49.95; // Exibe valores. cout
- Modifique o programa InClass.cpp, de maneira que os valores
usados para inicializar o objeto da classe Cliente sejam
solicitados do usurio. Private e public Teoria Todos os membros de
uma classe - dados e mtodos - so private por default. Isso
significa que eles somente podem ser acessados por mtodos da prpria
classe. Os membros public podem ser acessados atravs de qualquer
objeto da classe. Como princpio geral de projeto, devemos tornar os
membros de dados de uma classe private. Portanto, preciso criar
funes pblicas, conhecidas como mtodos de acesso, para definir e
acessar os valores das variveis private. Exemplo // PrivPub.cpp //
Ilustra o uso // de membros private // e public. #include // Define
uma classe. class Cliente { // Por default, estes membros // so
//private: int numCliente; float saldo; public: void
defineNumCliente(int num); int acessaNumCliente(); void
defineSaldo(float); float acessaSaldo(); }; // Fim de class
Cliente. int main() { // Cria um objeto // da classe cliente.
Cliente objCliente; // Atribui valores s // variveis do objeto //
cliente. objCliente.defineNumCliente(49);
objCliente.defineSaldo(6795.97); // Exibe valores. cout
-
} // Fim de Cliente::acessaNumCliente() void
Cliente::defineSaldo(float s) { saldo = s; } // Fim de
Cliente::defineSaldo() float Cliente::acessaSaldo() { return saldo;
} // Fim de Cliente::acessaSaldo() Exerccio Modifique o programa
PrivPub.cpp de maneira que os valores de inicializao do objeto da
classe Cliente sejam solicitados do usurio. Mtodos private Teoria
Embora o procedimento mais comum seja tornar os membros de dados
private e os mtodos public, nada impede que existam mtodos private.
Isso ditado pelas necessidades do projeto, e no por uma regra fixa.
O exemplo abaixo ilustra esse fato. Exemplo // MetPriv.cpp //
Ilustra o uso // de mtodos private. #include // Define uma classe.
class Cliente { // Por default, estes membros // so //private: int
numCliente; float saldo; // Estes mtodos tambm // so private. void
defineNumCliente(int num); int acessaNumCliente(); void
defineSaldo(float); float acessaSaldo(); public: void
inicializa(int, float); void exibe(); }; // Fim de class Cliente.
int main() { // Cria um objeto // da classe cliente. Cliente
objCliente; // Atribui valores s // variveis do objeto // cliente.
objCliente.inicializa(52, 99.09); // Exibe valores.
objCliente.exibe(); } // Fim de main() // Implementao dos mtodos.
void Cliente::defineNumCliente(int num) { numCliente = num; } //
Fim de Cliente::defineNumCliente() int
Cliente::acessaNumCliente()
- { return numCliente; } // Fim de Cliente::acessaNumCliente()
void Cliente::defineSaldo(float s) { saldo = s; } // Fim de
Cliente::defineSaldo() float Cliente::acessaSaldo() { return saldo;
} // Fim de Cliente::acessaSaldo() void Cliente::inicializa(int
num, float sal) { defineNumCliente(num); defineSaldo(sal); } // Fim
de Cliente::inicializa() void Cliente::exibe() { cout
- float saldo; // Estes mtodos tambm // so private. int
acessaNumCliente(); float acessaSaldo(); public: // Construtor
default. Cliente(); // Outro construtor. Cliente(int, float); //
Destrutor. ~Cliente(); // Um mtodo public. void exibe(); }; // Fim
de class Cliente. int main() { // Cria um objeto // da classe
cliente // sem definir valores. Cliente objCliente1; // Cria um
objeto // da classe cliente // inicializado. Cliente
objCliente2(572, 777.77); // Exibe valores. cout
-
Exerccio Modifique o programa Constr.cpp, de maneira que os
valores das variveis membro dos objetos sejam modificados aps a
criao dos objetos. Faa com que os novos valores sejam exibidos na
tela. Mtodos const Teoria Quando declaramos um mtodo como sendo
const, estamos indicando que esse mtodo no alterar o valor de
nenhum dos membros da classe. Para declarar um mtodo de classe como
sendo const, colocamos esta palavra-chave aps os parnteses ( ), mas
antes do caractere de ponto e vrgula ; Em geral, os mtodos de
acesso so declarados como const. Quando declaramos uma funo como
const, qualquer tentativa que faamos na implementao dessa funo de
alterar um valor do objeto ser sinalizada pelo compilador como
sendo um erro. Isso faz com que o compilador detecte erros durante
o processo de desenvolvimento, evitando a introduo de bugs no
programa. Exemplo // ConstMt.cpp // Ilustra o uso // de mtodos
const. #include // Define uma classe. class Cliente { // Por
default, estes membros // so //private: int numCliente; float
saldo; // Estes mtodos // so private e const. int
acessaNumCliente() const; float acessaSaldo() const; public: //
Construtor default. Cliente(); // Outro construtor. Cliente(int,
float); // Destrutor. ~Cliente(); // Mtodos public. void exibe()
const; void defineNumCliente(int); void defineSaldo(float); }; //
Fim de class Cliente. int main() { // Cria um objeto // da classe
cliente // sem definir valores. Cliente objCliente1; // Cria um
objeto // da classe cliente // inicializado. Cliente
objCliente2(572, 777.77); // Exibe valores.
- cout
-
Interface e implementao Teoria Fazer com que o compilador
detecte erros sempre uma boa idia. Isso impede que esses erros
venham a se manifestar mais tarde, na forma de bugs no programa.
Por isso, convm definir a interface de cada classe em um arquivo de
cabealho separado da implementao. Quando incluimos esse arquivo de
cabealho no cdigo do programa, utilizando a diretiva #include,
permitimos que o compilador faa essa checagem para ns, o que ajuda
bastante em nosso trabalho. Na verdade, esse o procedimento padro
em C++. O exemplo abaixo ilustra esse fato. Exemplo // IntImpl.h //
Ilustra a separao // de interface e // implementao em diferentes //
arquivos. #include // Define uma classe. class Cliente { // Por
default, estes membros // so //private: int numCliente; float
saldo; // Estes mtodos // so private e const. int
acessaNumCliente() const; float acessaSaldo() const; public: //
Construtor default. Cliente(); // Outro construtor. Cliente(int,
float); // Destrutor. ~Cliente(); // Mtodos public. void exibe()
const; void defineNumCliente(int); void defineSaldo(float); }; //
Fim de class Cliente.
//--------------------------------------------------
//-------------------------------------------------- // IntImpl.cpp
// Ilustra a separao // de interface e // implementao em diferentes
// arquivos. #include "IntImpl.h" int main() { // Cria um objeto //
da classe cliente // sem definir valores. Cliente objCliente1; //
Cria um objeto
- // da classe cliente // inicializado. Cliente objCliente2(572,
777.77); // Exibe valores. cout
-
Modifique o exemplo IntImpl.cpp/IntImpl.h, definindo os
seguintes mtodos como inline: acessaNumCliente(), acessaSaldo(),
defineNumCliente(), defineSaldo()
Objetos como membros Teoria Muitas vezes, construimos uma classe
complexa declarando classes mais simples e incluindo objetos dessas
classes simples na declarao da classe mais complicada. Por exemplo,
poderamos declarar uma classe roda, uma classe motor, uma classe
transmisso, e depois combinar objetos dessas classes para criar uma
classe carro, mais complexa. Chamamos esse tipo de relacionamento
tem-um, porque um objeto tem dentro de si outro objeto. Um carro
tem um motor, quatro rodas e uma transmisso. Exemplo
//------------------------------------------------- // ObjMemb.h //
Ilustra uma classe // que tem objetos // de outra classe como //
membros. #include class Ponto { int coordX; int coordY; public:
void defineX(int vlrX) { coordX = vlrX; } // Fim de defineX() void
defineY(int vlrY) { coordY = vlrY; } // Fim de defineY() int
acessaX() const { return coordX; } // Fim de acessaX() int
acessaY() const { return coordY; } // Fim de acessaY() }; // Fim de
class Ponto. // No sistema de coords considerado, // a origem (0,
0) fica no canto // superior esquerdo. class Retangulo { Ponto
supEsq; Ponto infDir; public: // Construtor. Retangulo(int esq, int
topo, int dir, int base); // Destrutor. ~Retangulo() { cout
-
} // Fim de ~Retangulo() // Funes de acesso. Ponto
acessaSupEsq() const { return supEsq; } // Fim de acessaSupEsq()
const Ponto acessaInfDir() const { return infDir; } // Fim de
acessainfDir() const // Funes para definir // valores. void
defineSupEsq(Ponto se) { supEsq = se; } // Fim de defineSupEsq()
void defineInfDir(Ponto id) { infDir = id; } // Fim de
defineInfDir() // Funo para calcular // a rea do retngulo. int
calcArea() const; }; // Fim de class Retangulo.
//--------------------------------------------------
//-------------------------------------------------- // ObjMemb.cpp
// Ilustra uma classe // que tem objetos // de outra classe como //
membros. #include "ObjMemb.h" // Implementaes.
Retangulo::Retangulo(int esq, int topo, int dir, int base) {
supEsq.defineY(topo); supEsq.defineX(esq); infDir.defineY(base);
infDir.defineX(dir); } // Fim de Retangulo::Retangulo() int
Retangulo::calcArea() const { int xd, xe, ys, yi; xd =
infDir.acessaX(); xe = supEsq.acessaX(); yi = infDir.acessaY(); ys
= supEsq.acessaY(); return (xd - xe) * (yi - ys); } // Fim de
Retangulo::calcArea() const int main() { // Solicita coords para //
o retangulo. int xse, yse, xid, yid; cout > xse; cout > yse;
cout > xid; cout > yid; // Cria um objeto
- // da classe Retangulo. Retangulo ret(xse, yse, xid, yid); int
areaRet = ret.calcArea(); cout
- { cout
- { cout
- // via ponteiro. #include // Define uma classe. class Cliente {
int idCliente; float saldo; public: // Construtor. Cliente(int id,
float sal) { cout
- Modifique o exemplo ViaPont.cpp, de maneira que os valores das
variveis membro dos objetos criados sejam alterados aps a criao.
Para isso, inclua na classe Cliente dois mtodos para modificar os
valores das variveis: void defineId(int id) e void
defineSaldo(float sal). Faa com que os novos valores sejam exibidos
na tela. Ponteiros como membros de uma classe Teoria Um ou mais
membros de uma classe podem ser ponteiros para objetos do free
store. A memria pode ser alocada no construtor da classe, ou em um
de seus mtodos, e pode ser liberada no construtor. Exemplo
//----------------------------------------------------- //
MembPnt.cpp // Ilustra ponteiros como // membros de dados // de uma
classe. #include // Define uma classe. class Cliente { // Estes
membros // so ponteiros. int* pIdCliente; float* pSaldo; public: //
Construtor. Cliente(int id, float sal); // Destrutor. ~Cliente();
// Um mtodo para // exibir valores do // cliente. void
mostraCliente() { cout
- { cout
- a uma funo membro, ou em cada acesso a um membro de dados, o
ponteiro this passado como um parmetro oculto. Veja o exemplo.
Exemplo //-------------------------------------------------------
// ThisPnt.cpp // Ilustra o uso do // ponteiro this. #include //
Define uma classe. class Cliente { int idCliente; float saldo;
public: // Construtor. Cliente(int id, float sal) { cout idCliente
= id; this->saldo = sal; } // Fim de Cliente() // Destrutor.
~Cliente() { cout
-
} // Fim de if. // Cria uma varivel local Cliente. cout
mostraCliente(); // O mesmo que: //(*pCliente).mostraCliente();
clienteLocal.mostraCliente(); // Altera valores. cout defineId(40);
pCliente->defineSaldo(400.0); clienteLocal.defineId(80);
clienteLocal.defineSaldo(800.0); // Exibe os novos // valores dos
objetos. cout mostraCliente(); clienteLocal.mostraCliente(); //
Deleta Cliente dinmico. delete pCliente; return 0; } // Fim de
main() //----------------------------------------------------
Exerccio Modifique o exemplo ThisPnt.cpp, declarando uma classe
chamada Retangulo. A classe Retangulo deve conter uma funo membro
para calcular a rea do retngulo. Utilize esse mtodo para calcular a
rea de um objeto Retangulo, usando o ponteiro this em todos os
acessos a funes membro e variveis membro da classe Retangulo.
Referncias a objetos Teoria Podemos ter referndia a qualquer
tipo de varivel, inclusive variveis de tipos definidos pelo usurio.
Observe que podemos criar uma referncia a um objeto, mas no a uma
classe. No poderamos escrever: int & refInt = int; // Erro!!!
preciso inicializar refInt com uma determinada varivel inteira,
como por exemplo: int intVar = 100; int & refInt = intVar; Da
mesma forma, no podemos inicializar uma referncia com a classe
Cliente: Cliente & refCliente = Cliente; // Erro!!! Precisamos
inicializar refCliente com um objeto Cliente em particular. Cliente
fulano; Cliente & refCliente = fulano; As referncias a objetos
so usadas da mesma forma que o prprio objeto. Os membros de dados e
os mtodos so acessados usando-se o operador ponto .
- Da mesma forma que no caso dos tipos simples, a referncia
funciona como um sinnimo para o objeto. Exemplo
//--------------------------------------------------------- //
RefClas.cpp // Ilustra o uso de referncias // a objetos de uma
classe. #include // Declara uma classe. class Cliente { int
idCliente; int saldo; public: // Construtor. Cliente(int id, int
sal); // Destrutor. ~Cliente() { cout
-
No exemplo RefClass.cpp, modifique os valores do objeto
umCliente usando a referncia. Em seguida, exiba os novos valores
(a) usando o objeto (b) usando a referncia.
Funes membro sobrecarregadas Teoria Vimos no Curso C++ Bsico que
podemos implementar a sobrecarga de funes, escrevendo duas ou mais
funes com o mesmo nome, mas com diferentes listas de parmetros. As
funes membros de classes podem tambm ser sobrecarregadas, de forma
muito similar, conforme mostrado no exemplo abaixo. Exemplo
//----------------------------------------------- // SbrMemb.cpp //
Ilustra sobrecarga // de funes membro. #include class Retangulo {
int altura; int largura; public: // Construtor. Retangulo(int alt,
int larg); // Funo sobrecarregada. void desenha(); void
desenha(char c); }; // Fim de class Retangulo. // Implementao. //
Construtor. Retangulo::Retangulo(int alt, int larg) { altura = alt;
largura = larg; } // Fim de Retangulo::Retangulo() // Funo
sobrecarregada. void Retangulo::desenha() // Desenha o retngulo
preenchendo-o // com o caractere '*' { for(int i = 0; i <
altura; i++) { for(int j = 0; j < largura; j++) cout
- cout
- void Retangulo::desenha(char c) // Desenha o retngulo
preenchendo-o // com o caractere c { for(int i = 0; i < altura;
i++) { for(int j = 0; j < largura; j++) cout
- public: // Construtores sobrecarregados. // Default.
Retangulo(); Retangulo(int alt, int larg); // Funo com valor //
default. void desenha(char c = '*'); }; // Fim de class Retangulo.
// Implementao. // Construtor default. Retangulo::Retangulo() {
altura = 7; largura = 11; } // Fim de Retangulo::Retangulo()
Retangulo::Retangulo(int alt, int larg) { altura = alt; largura =
larg; } // Fim de Retangulo::Retangulo() // Funo com valor default.
void Retangulo::desenha(char c) // Desenha o retngulo preenchendo-o
// com o caractere c { for(int i = 0; i < altura; i++) { for(int
j = 0; j < largura; j++) cout
- At agora, temos definido os valores das variveis membro dentro
do corpo do construtor. Porm os construtores so chamados em dois
estgios: o estgio de inicializao e o corpo da funo. A maioria das
variveis pode ter seus valores definidos em qualquer dos dois
estgios. Porm mais eficiente, e mais elegante, inicializar as
variveis membro no estgio de inicializao. O exemplo abaixo ilustra
como isso feito. Exemplo
//-------------------------------------------------------- //
InicVar.cpp // Ilustra inicializao // de variveis membro. #include
class Retangulo { int altura; int largura; public: // Construtores
sobrecarregados. // Default. Retangulo(); Retangulo(int alt, int
larg); // Funo com valor // default. void desenha(char c = '*'); };
// Fim de class Retangulo. // Implementao. // Construtor default.
Retangulo::Retangulo() : altura(7), largura(11) { cout
- ret1.desenha('1'); cout
- // CopyCnt.cpp // Ilustra uso do // construtor de cpia.
#include class Retangulo { int altura; int largura; public: //
Construtores sobrecarregados. // Default. Retangulo(); // Cpia.
Retangulo(const Retangulo&); Retangulo(int alt, int larg); //
Funo com valor // default. void desenha(char c = '*'); }; // Fim de
class Retangulo. // Implementao. // Construtor default.
Retangulo::Retangulo() : altura(7), largura(11) { cout
-
retCopia.desenha('C'); return 0; } // Fim de main()
//-------------------------------------------------- Exerccio
Modifique o exemplo CopyCnt.cpp utilizando no construtor de cpia a
notao que inicializa as variveis antes do incio do corpo do
construtor.
Sobrecarregando o operador ++ Teoria Cada um dos tipos embutidos
de C++, como int, float e char, tem diversos operadores que se
aplicam a esse tipo, como o operador de adio (+) e o operador de
multiplicao (*). C++ permite que o programador crie tambm
operadores para suas prprias classes, utilizando a sobrecarga de
operadores. Para ilustrar o uso da sobrecarga de operadores,
comearemos criando uma nova classe, chamada Contador. Um objeto
Contador poder ser usado em loops e outras situaes nas quais um
nmero deve ser incrementado, decrementado e ter seu valor acessado.
Por enquanto, os objetos de nossa classe Contador no podem ser
incrementados, decrementados, somados, atribudos nem manuseados de
outras formas. Nos prximos passos, acrescentaremos essa
funcionalidade a nossa classe. Exemplo
//-------------------------------------------------------- //
SobreO.cpp // Ilustra sobrecarga // de operadores. #include //
Declara a classe Contador. class Contador { unsigned int vlrCont;
public: // Construtor. Contador(); // Destrutor. ~Contador();
unsigned int acessaVal() const; void defineVal(unsigned int val);
}; // Fim de class Contador. // Implementao. // Construtor.
Contador::Contador() : vlrCont(0) { cout
- } // Fim de Contador::acessaVal() void
Contador::defineVal(unsigned int val) { vlrCont = val; } // Fim de
Contador::defineVal() int main() { // Um objeto contador. Contador
umCont; // Exibe valor. cout
- return vlrCont; } // Fim de Contador::acessaVal() void
Contador::defineVal(unsigned int val) { vlrCont = val; } // Fim de
Contador::defineVal() void Contador::mostraVal() const { cout
- void mostraVal() const; // Sobrecarrega operador. void
operator++(); }; // Fim de class Contador. // Implementao. //
Construtor. Contador::Contador() : vlrCont(0) { cout
- no retorna um objeto Contador. No podemos atribuir um objeto
void a um objeto Contador. claro que o que precisamos fazer com que
o operador ++ retorne um objeto Contador. Exemplo
//--------------------------------------------------- //
SobreO6.cpp // Ilustra sobrecarga // de operadores. // Agora
operador++ // retorna um objeto // temporrio. #include // Declara a
classe Contador. class Contador { unsigned int vlrCont; public: //
Construtor. Contador(); // Destrutor. ~Contador(); unsigned int
acessaVal() const; void defineVal(unsigned int val); void
mostraVal() const; Contador operator++(); }; // Fim de class
Contador. // Implementao. // Construtor. Contador::Contador() :
vlrCont(0) { cout
- { // Dois objetos Contador. Contador cont1, cont2; // Exibe
valor. cout
- vlrCont(umCont.vlrCont) { cout
- vlrCont. O ponteiro this pode ento ser de-referenciado,
evitando assim a necessidade de criao de um objeto temporrio.
Exemplo //-------------------------------------------------------
// SobreO10.cpp // Ilustra sobrecarga // de operadores. // Agora
operador++ // utiliza o ponteiro this // para retornar um objeto.
#include // Declara a classe Contador. class Contador { unsigned
int vlrCont; public: // Construtor. Contador(); // Destrutor.
~Contador(); unsigned int acessaVal() const; void
defineVal(unsigned int val); void mostraVal() const; const
Contador& operator++(); }; // Fim de class Contador. //
Implementao. // Construtor. Contador::Contador() : vlrCont(0) {
cout
- // Exibe valor. cout
- Contador(); // Destrutor. ~Contador(); unsigned int acessaVal()
const; void defineVal(unsigned int val); void mostraVal() const; //
Sufixo. const Contador operator++(int); }; // Fim de class
Contador. // Implementao. // Construtor. Contador::Contador() :
vlrCont(0) { } // Fim de Contador::Contador() // Destrutor.
Contador::~Contador() { } // Fim de Contador::~Contador() unsigned
int Contador::acessaVal() const { return vlrCont; } // Fim de
Contador::acessaVal() void Contador::defineVal(unsigned int val) {
vlrCont = val; } // Fim de Contador::defineVal() void
Contador::mostraVal() const { cout
-
Modifique o exemplo SobreO12.cpp, para que o operador ++ seja
usado nas duas posies, prefixo e sufixo.
Sobrecarregando o operador + Teoria Vimos como sobrecarregar o
operador ++, que unrio. Ele opera somente sobre um objeto. O
operador de adiao + um operador binrio, que trabalha com dois
objetos. O objetivo aqui poder declarar duas variveis Contador e
poder som-las como no exemplo: Contador c1, c2, c3; c3 = c2 + c1;
Comearemos escrevendo uma funo soma(), que recebe um Contador como
argumento, soma os valores e depois retorna um Contador como
resultado. Exemplo
//----------------------------------------------------------
// SobrePl.cpp // Ilustra sobrecarga // de operadores. //
Ilustra sobrecarga do // operador + // Inicialmente, funo soma()
#include // Declara a classe Contador. class Contador { unsigned
int vlrCont; public: // Construtor. Contador(); // Construtor com
inicializao. Contador(unsigned int vlr); // Destrutor. ~Contador();
unsigned int acessaVal() const; void defineVal(unsigned int val);
void mostraVal() const; // A funo soma() Contador soma(const
Contador&); }; // Fim de class Contador. // Implementao. //
Construtor. Contador::Contador() : vlrCont(0) { } // Fim de
Contador::Contador() // Construtor com inicializao.
Contador::Contador(unsigned int vlr) : vlrCont(vlr) { } // Fim de
Contador::Contador(unsigned int) // Destrutor.
Contador::~Contador() { } // Fim de Contador::~Contador() unsigned
int Contador::acessaVal() const
- { return vlrCont; } // Fim de Contador::acessaVal() void
Contador::defineVal(unsigned int val) { vlrCont = val; } // Fim de
Contador::defineVal() void Contador::mostraVal() const { cout
- void defineVal(unsigned int val); void mostraVal() const; // O
operador + Contador operator+(const Contador&); }; // Fim de
class Contador. // Implementao. // Construtor. Contador::Contador()
: vlrCont(0) { } // Fim de Contador::Contador() // Construtor com
inicializao. Contador::Contador(unsigned int vlr) : vlrCont(vlr) {
} // Fim de Contador::Contador(unsigned int) // Destrutor.
Contador::~Contador() { } // Fim de Contador::~Contador() unsigned
int Contador::acessaVal() const { return vlrCont; } // Fim de
Contador::acessaVal() void Contador::defineVal(unsigned int val) {
vlrCont = val; } // Fim de Contador::defineVal() void
Contador::mostraVal() const { cout
-
Teoria O operador = um dos operadores fornecidos por default
pelo compilador, mesmo para os tipos (classes) definidos pelo
programador. Mesmo assim, pode surgir a necessidade de
sobrecarreg-lo. Quando sobrecarregamos o operador =, precisamos
levar em conta um aspecto adicional. Digamos que temos dois objetos
Contador, c1 e c2. Com o operador de atribuio, podemos atribuir c2
a c1, da seguinte forma: c1 = c2; O que acontece se uma das
variveis membro for um ponteiro? E o que acontece com os valores
originais de c1? Lembremos o conceito de cpia rasa e cpia profunda.
Uma cpia rasa apenas copia os membros, e os dois objetos acabam
apontando para a mesma rea do free store. Uma cpia profunda aloca a
memria necessria. H ainda outra questo. O objeto c1 j existe na
memria, e tem sua memria alocada. Essa memria precisa ser deletada,
para evitar vazamentos de memria. Mas o que acontece se atribuirmos
um objeto a si mesmo, da seguinte forma: c1 = c1; Ningum vai fazer
isso de propsito, mas se acontecer, o programa precisa ser capaz de
lidar com isso. E mais importante, isso pode acontecer por
acidente, quando referncias e ponteiros de-referenciados ocultam o
fato de que a atribuio est sendo feita ao prprio objeto. Se essa
questo no tiver sido tratada com cuidado, c1 poder deletar sua
memria alocada. Depois, no momento de copiar a memria do lado
direito da atribuio, haver um problema: a memria ter sido deletada.
Para evitar esse problema, o operador de atribuio deve checar se o
lado direito do operador de atribuio o prprio objeto. Isso feito
examinando o ponteiro this. O exemplo abaixo ilustra esse
procedimento. Exemplo
//------------------------------------------------------- //
SobreAtr.cpp // Ilustra sobrecarga // de operadores. // Implementa
operador = #include // Declara a classe Contador. class Contador {
unsigned int vlrCont; public: // Construtor. Contador(); //
Construtor com inicializao. Contador(unsigned int vlr); //
Destrutor. ~Contador(); unsigned int acessaVal() const; void
defineVal(unsigned int val); void mostraVal() const; // O operador
= Contador& operator=(const Contador&);
- }; // Fim de class Contador. // Implementao. // Construtor.
Contador::Contador() : vlrCont(0) { } // Fim de
Contador::Contador() // Construtor com inicializao.
Contador::Contador(unsigned int vlr) : vlrCont(vlr) { } // Fim de
Contador::Contador(unsigned int) // Destrutor.
Contador::~Contador() { } // Fim de Contador::~Contador() unsigned
int Contador::acessaVal() const { return vlrCont; } // Fim de
Contador::acessaVal() void Contador::defineVal(unsigned int val) {
vlrCont = val; } // Fim de Contador::defineVal() void
Contador::mostraVal() const { cout
- Converso entre objetos e tipos simples Teoria O que acontece
quando tentamos converter uma varivel de um tipo simples, como int,
em um objeto de uma classe definida pelo programador? A classe para
a qual desejamos converter o tipo simples precisar ter um
construtor especial, com essa finalidade. Esse construtor dever
receber como argumento o tipo simples a ser convertido. O exemplo
abaixo ilustra essa situao. Exemplo
//-------------------------------------------------------- //
ConvObj.cpp // Ilustra converso de // um tipo simples // em um
objeto. // ATENO: ESTE PROGRAMA // CONTM UM ERRO DELIBERADO.
#include // Declara a classe Contador. class Contador { unsigned
int vlrCont; public: // Construtor. Contador(); // Destrutor.
~Contador(); unsigned int acessaVal() const; void
defineVal(unsigned int val); void mostraVal() const; }; // Fim de
class Contador. // Implementao. // Construtor. Contador::Contador()
: vlrCont(0) { } // Fim de Contador::Contador() // Destrutor.
Contador::~Contador() { } // Fim de Contador::~Contador() unsigned
int Contador::acessaVal() const { return vlrCont; } // Fim de
Contador::acessaVal() void Contador::defineVal(unsigned int val) {
vlrCont = val; } // Fim de Contador::defineVal() void
Contador::mostraVal() const { cout
- // Uma varivel unsigned int. unsigned int uiVar = 50; // Um
objeto Contador. Contador cont; // Tenta converter unsigned int //
em contador. cont = uiVar; // Exibe valor. cout
- Contador::~Contador() { } // Fim de Contador::~Contador()
unsigned int Contador::acessaVal() const { return vlrCont; } // Fim
de Contador::acessaVal() void Contador::defineVal(unsigned int val)
{ vlrCont = val; } // Fim de Contador::defineVal() void
Contador::mostraVal() const { cout
- #include class Cliente { int numCliente; float saldo; public:
// Construtor. Cliente(); int acessaNum() const; float
acessaSaldo() const; void defineNum(int num); void
defineSaldo(float sal); }; // Fim de class Cliente. // Definies.
Cliente::Cliente() { numCliente = 0; saldo = 0.0; } // Fim de
Cliente::Cliente() int Cliente::acessaNum() const { return
numCliente; } // Fim de Cliente::acessaNum() float
Cliente::acessaSaldo() const { return saldo; } // Fim de
Cliente::acessaSaldo() void Cliente::defineNum(int num) {
numCliente = num; } // Fim de Cliente::defineNum() void
Cliente::defineSaldo(float sal) { saldo = sal; } // Fim de
Cliente::defineSaldo() int main() { // Um array de clientes.
Cliente arrayClientes[5]; // Inicializa. for(int i = 0; i < 5;
i++) { arrayClientes[i].defineNum(i + 1);
arrayClientes[i].defineSaldo((float)(i + 1) * 25); } // Fim de
for(int i... // Exibe. for(int i = 0; i < 5; i++) { cout
-
Uma classe string Teoria Atualmente, todos os compiladores C++
em conformidade com o padro ANSI/ISO vm com uma classe string, o
que facilita bastante a manipulao de strings. Entretanto, como
exerccio de programao vamos implementar nossa prpria classe string.
Para evitar confuses, vamos cham-la de ClString. Exemplo
//-----------------------------------------------------------
// ClStr.cpp // Ilustra um exemplo // de classe string. #include
#include // Declara a classe. class ClString { // A string
propriamente dita. char* str; // O comprimento da string. unsigned
int compr; // Um construtor private. ClString(unsigned int);
public: // Construtores. ClString(); ClString(const char* const);
ClString(const ClString&); // Destrutor. ~ClString(); //
Operadores sobrecarregados. char& operator[](unsigned int
posicao); char operator[](unsigned int posicao) const; ClString
operator+(const ClString&); void operator +=(const
ClString&); ClString& operator =(const ClString&); //
Mtodos de acesso. unsigned int acessaCompr() const { return compr;
} // Fim de acessaCompr() const char* acessaStr() const { return
str; } // Fim de acessaStr() }; // Fim de class ClString. //
Implementaes. // Construtor default. // Cria uma string de //
comprimento zero. ClString::ClString() { // cout
- // Usado somente pelos // mtodos da classe. // Cria uma string
com o // comprimento especificado // e a preenche com o //
caractere '\0' ClString::ClString(unsigned int comp) { // cout
- else return str[pos]; } // Fim de ClString::operator[]() //
Operador de posio constante. // Para uso em objetos const. char
ClString::operator[](unsigned int pos) const { if(pos > compr)
return str[compr - 1]; else return str[pos]; } // Fim de
ClString::operator[]() // Concatena duas strings. ClString
ClString::operator+(const ClString& strRef) { unsigned int
comprTotal = compr + strRef.acessaCompr(); ClString
tempStr(comprTotal); unsigned int i; for(i = 0; i < compr; i++)
tempStr[i] = str[i]; for(unsigned int j = 0; j <
strRef.acessaCompr(); j++, i++) tempStr[i] = strRef[j];
tempStr[comprTotal] = '\0'; return tempStr; } // Fim de
ClString::operator+() void ClString::operator+=(const ClString&
strRef) { unsigned int comprRef = strRef.acessaCompr(); unsigned
int comprTotal = compr + comprRef; ClString tempStr(comprTotal);
unsigned int i; for(i = 0; i < compr; i++) tempStr[i] = str[i];
for(unsigned int j = 0; j < strRef.acessaCompr(); j++, i++)
tempStr[i] = strRef[i - compr]; tempStr[comprTotal] = '\0'; *this =
tempStr; } // Fim de ClString::operator+=() int main() { //
Constroi string a partir // de array de char. ClString
str1("Tarcisio Lopes"); // Exibe. cout
- cout
-
// objetos que formaro // a lista. class Cliente { int
numCliente; public: // Construtores. Cliente() {numCliente = 1;}
Cliente(int num) : numCliente(num){} // Destrutor. ~Cliente(){} //
Mtodo de acesso. int acessaNum() const {return numCliente;} }; //
Fim de class Cliente. // Uma classe para gerenciar e // ordenar a
lista. class No { Cliente* pCliente; No* proxNo; public: //
Construtor. No(Cliente*); // Destrutor. ~No(); // Outros mtodos.
void defineProx(No* pNo) {proxNo = pNo;} No* acessaProx() const
{return proxNo;} Cliente* acessaCli() const {return pCliente;} void
insere(No*); void Exibe(); }; // Fim de class No. // Implementao.
// Construtor. No::No(Cliente* pCli): pCliente(pCli), proxNo(0) { }
// Fim de No::No(Cliente*) // Destrutor. No::~No() { cout
acessaCli()->acessaNum(); int novoNum =
novoNo->acessaCli()->acessaNum(); int numDeste =
pCliente->acessaNum(); assert(novoNum >= numDeste);
- if(novoNum < numProxCli) { novoNo->defineProx(proxNo);
proxNo = novoNo; } // Fim de if(novoNum < numProxCli) else
proxNo->insere(novoNo); } // Fim de else (externo) } // Fim de
No::insere(No*) void No::Exibe() { if(pCliente->acessaNum() >
0) { cout Exibe(); } // Fim de No::Exibe() int main() { // Um
ponteiro para n. No* pNo; // Um ponteiro para // Cliente,
inicializado. Cliente* pontCli = new Cliente(0); // Um array de
ints para // fornecer nmeros de clientes. int numeros[] = {19, 48,
13, 17, 999, 18, 7, 0}; // Exibe nmeros. cout
- No mundo real, muitas vezes as coisas so organizadas em
hierarquias. Eis alguns exemplos de hierarquias do mundo real: Um
carro e um caminho so veculos automotores; um veculo automotor um
meio de transporte; um meio de transporte uma mquina. Dlmata,
pequins e pastor alemo so raas de ces; um co um mamfero, tal como
um gato e uma baleia; um mamfero um animal; um animal um ser vivo.
Um planeta um astro; astros podem ou no ter luz prpria; todo astro
um corpo celeste. Uma hierarquia estabelece um relacionamento do
tipo -um. Um cachorro um tipo de canino. Um fusca um tipo de carro,
que um tipo de veculo automotor. Um sorvete um tipo de sobremesa,
que um tipo de alimento. O que significa dizer que uma coisa um
tipo de outra coisa? Significa que uma coisa uma forma mais
especializada de outra. Um carro um tipo especializado de veculo
automotor. Um caminho outra forma especializada de veculo
automotor. Um trator ainda outra forma especializada de veculo
automotor. Exemplo
//------------------------------------------------------- //
IntrHer.cpp // Apresenta o uso // de herana. #include class
ClasseBase { protected: int m_propr_base1; int m_propr_base2;
public: // Construtores. ClasseBase() : m_propr_base1(10),
m_propr_base2(20) {} // Destrutor. ~ClasseBase() {} // Mtodos de
acesso. int acessaPropr1() const {return m_propr_base1;} void
definePropr1(int valor){ m_propr_base1 = valor;} int acessaPropr2()
const {return m_propr_base2;} void definePropr2(int
valor){m_propr_base2 = valor;} // Outros mtodos. void met_base1()
const { cout
- // Mtodos de acesso. int acessaPropr_deriv() const { return
m_propr_deriv; } // Fim de acessaPropr_deriv() void
definePropr_deriv(int valor) { m_propr_deriv = valor; } // Fim de
definePropr_deriv() // Outros mtodos. void metodoDeriv1() {
cout
- // Ilustra a ordem // de chamada a // construtores. #include
class ClasseBase { protected: int m_propr_base1; int m_propr_base2;
public: // Construtores. ClasseBase() : m_propr_base1(10),
m_propr_base2(20) { cout
- } // Fim de metodoDeriv1() void metodoDeriv2() { cout
- // Destrutor. ~ClasseBase(); // Mtodos de acesso. int
acessaPropr1() const {return m_propr_base1;} void definePropr1(int
valor){ m_propr_base1 = valor;} int acessaPropr2() const {return
m_propr_base2;} void definePropr2(int valor){m_propr_base2 =
valor;} // Outros mtodos. void met_base1() const { cout
- cout
- Exemplo
//-------------------------------------------------------- //
Overrd.cpp // Apresenta o uso // da superposio // de mtodos
(overriding) #include class ClasseBase { protected: int
m_propr_base1; int m_propr_base2; public: // Construtores.
ClasseBase() : m_propr_base1(10), m_propr_base2(20) {} //
Destrutor. ~ClasseBase() {} // Mtodos de acesso. int acessaPropr1()
const {return m_propr_base1;} void definePropr1(int valor){
m_propr_base1 = valor;} int acessaPropr2() const {return
m_propr_base2;} void definePropr2(int valor){m_propr_base2 =
valor;} // Outros mtodos. void met_base1() const { cout
- void met_base2() /*const*/; }; // Fim de class ClasseDeriv. //
Implementaes. void ClasseDeriv::met_base1() /*const*/ { cout
- cout
- // AcsOcul.cpp // Ilustra acesso a // mtodos ocultos na //
classe base. #include class ClasseBase { protected: int
m_propr_base1; int m_propr_base2; public: void met_base() const {
cout
- Atravs do polimorfismo, C++ permite que ponteiros para a classe
base sejam atribudos a objetos da classe derivada. Portanto,
perfeitamente legal escrever: Mamifero* pMamifero = new Cachorro;
Estamos criando um novo objeto da classe Cachorro no free store, e
atribuindo o ponteiro retornado por new a um ponteiro para
Mamifero. No h nenhum problema aqui: lembre-se, um Cachorro um
Mamifero. Podemos usar esse ponteiro para invocar mtodos da classe
Mamifero. Mas seria tambm desejvel poder fazer com que os mtodos
superpostos em Cachorro chamassem a verso correta da funo. Isso
possvel com o uso de funes virtuais. Veja o exemplo. Exemplo
//-------------------------------------------------------- //
Virt.cpp // Ilustra o uso de mtodos // virtuais. #include class
Mamifero { protected: int m_idade; public: // Construtor.
Mamifero(): m_idade(1) { cout
-
// superposto. pMam->andar(); // Chama o mtodo // virtual
superposto. pMam->emiteSom(); return 0; } // Fim de main()
//-------------------------------------------------
Chamando mltiplas funes virtuais Teoria Como funcionam as funes
virtuais? Quando um objeto derivado, como o objeto Cachorro,
criado, primeiro chamado o construtor da classe base Mamifero;
depois chamado o construtor da prpria classe derivada Cachorro.
Assim, o objeto Cachorro contm em si um objeto da classe base
Mamifero. As duas partes do objeto Cachorro ficam armazenadas em
pores contguas da memria. Quando uma funo virtual criada em um
objeto, o objeto deve manter controle sob essa nova funo. Muitos
compiladores utilizam uma tabela de funes virtuais, chamada
v-table. Uma v-table mantida para cada tipo, e cada objeto desse
tipo mantm um ponteiro para a v-table. Esse ponteiro chamado vptr,
ou v-pointer). Assim, o vptr de cada objeto aponta para a v-table
que, por sua vez, tem um ponteiro para cada uma das funes virtuais.
Quando a parte Mamifero de um objeto Cachorro criada, o vptr
inicializado para apontar para a parte certa da v-table. Quando o
construtor de Cachorro chamado e a parte Cachorro do objeto
acrescentada, o vptr ajustado para apontar para as funes virtuais
superpostas, se houver, no objeto Cachorro. Exemplo
//-------------------------------------------------------- //
MulVirt.cpp // Ilustra chamada a // mltiplas verses // de um mtodo
virtual. #include class Mamifero { protected: int idade; public: //
Construtor. Mamifero() : idade(1) { } // Destrutor. ~Mamifero() {}
// Mtodo virtual. virtual void emiteSom() const { cout
- class Cachorro : public Mamifero { public: // Implementa mtodo
virtual. void emiteSom() const {cout
- mamPtr->emiteSom(); break; default: cout
- void funcaoPorRef(Mamifero&); int main() { Mamifero*
mamPtr; int opcao; cout
- Como cada classe derivada superpe o mtodo clone(), uma cpia do
objeto correto criada. Exemplo
//------------------------------------------------------- //
VirtCop.cpp // Ilustra uso do // mtodo clone() // como substituto
para // um construtor de cpia // virtual. #include class Mamifero {
public: Mamifero() : idade(1) { cout
- } // Fim de emiteSom() virtual Mamifero* clone() { return new
Cachorro(*this); } // Fim de clone() }; // Fim de class Cachorro.
// Construtor de cpia. Cachorro::Cachorro(const Cachorro&
refCach) : Mamifero(refCach) { cout
-
break; } // Fim de switch. // Um outro ponteiro // para
Mamifero. Mamifero* mamPtr2; cout emiteSom(); // Cria clone.
mamPtr2 = mamPtr->clone(); cout emiteSom(); return 0; } // Fim
de main()
//----------------------------------------------------