Anda di halaman 1dari 44

Renato Cardoso Mesquita

Departamento de Eng.
Eltrica da UFMG
renato@cpdee.ufmg.br

Programao Orientada a Objetos


em C++

3- Classes em C++
.

(Montenegro, parte IV)


3.1 Implementao de Classes
3.1.1. Estruturas e classes:
struct Funcionario {
char nome[20];
char endereco[20];
int idade;
};
class Cliente {
public:
char nome[20];
char endereco[20];
int idade;
};
Semelhanas:
Funcionario f1, *f2;
Cliente c1, *c2;

Classes em C++ Pg. 2


strcpy(f1.nome, "Jorge");
strcpy(f2->endereco, "Rua Pernambuco 453");
strcpy(c2->nome, "Renato");
c1.idade = 20;
Diferenas:
Restries de acesso;
Em C++ as Classes (e tambm as Struct) podem ter funes como
membros!
Definio de classes:
class Nome_da_classe {
tipo de acesso:
tipo_1 nome_membro_1;
tipo_2 nome_membro_2;
...
tipo_n nome_membro_n;
prottipo_de_funo_1
prottipo_de_funo_2
...
prottipo_de_funo_m
};

Exemplo:
class Ponto {
private:
int x,y;
public:
void init(int,int);
void draw();
void moveto(int,int);
};
Ponto A,B;
B.init(10,20);
A.init(300,300);
A.moveto(100,100);
B.draw();
A.x; B.y; // ERRO!!!!!!!!!

OBS:
Tipos de acesso: private, public e protected
Default: private
Poltica a ser seguida: confinar as informaes de uma classe prpria
classe, acessando-as atravs de suas funes membros
encapsulamento!
Declarao de objetos da classe (A, B);
Classes membro de classes (Estrutura Todo-Parte).
Exemplo:
class Circulo{
private:
int raio;
Ponto centro;
public:

Classes em C++ Pg. 3


void init(int, int, int);
void draw();
void moveto(int, int);
};
Circulo circ1;
circ1.init(10,20,5);
circ1.draw();
circ1.moveto(30,50);
circ1.draw();
OBS: se o acesso fosse todo pblico, poderamos escrever
circ1.centro.x no acessvel por se tratar de membro privado!
3.1.2. Funes membro:
Funes membro de uma classe so declaradas na definio da classe;
Definio das funes membro obedece a seguinte sintaxe:
Tipo_de_retorno Nome_ classe :: funo_membro(tipo(s) parmetros){
// Cdigo da funo
}
Exemplo:
void Ponto :: moveto(int x1, int y1){
x = x1;
y = y1;

// Acesso aos membros protegidos de Ponto !!


// x e y de quem ???

}
void Circulo :: moveto(int x1, int y1){
centro.moveto(x1,y1);
// No conseguiria acessar diretamente x e y
// de centro, pois estes so membros privados de
// Ponto
}

Classes em C++ Pg. 4


Funes membro inline:
a) Definida na classe:
class Circulo{
private:
int raio;
Ponto centro;
public:
void init(int, int, int);
void show();
void moveto(int, int);
float area(void){return PI*raio*raio;}
};

b)Definida fora da classe:


class Circulo{
...
public:
...
float area(void);
};
...
// No se pode chamar area aqui
inline float Circulo::area(void)
{return PI*raio*raio;}

OBS: Cuidado com a sintaxe errada apresentada na pgina 161!!!!!


Mais um exemplo:
class Date {
int d, m, y;
public:
void init (int dd, int mm, int yy);
void add_year(int n);
void add_month(int n);
void add_day(int n);
// ....
};

// Inicializa
// Adiciona n anos
// Adiciona n meses
// Adiciona n dias
// Outros mtodos

Comentrios:
Parte pblica de uma classe define a sua interface com o mundo, isto
, como ela pode ser utilizada, como seu estado pode ser modificado,
etc
Vantagens de se restringir o acesso aos membros privados: evitam-se
inicializaes e modificaes ilegais (exemplo, inicializar com
30/02/98, mudar o nmero do ms para 14) e se garante que as
operaes sejam feitas corretamente (exemplo, ao se somar n dias ao
dia atual, verifica-se se d ultrapassa o nmero mximo de dias do ms
m, fazendo-se a atualizao em d e m, simultaneamente).
Todo e qualquer mudana no estado de um objeto Date pode e deve
ser efetuada por suas funes membro

Classes em C++ Pg. 5


Se mudarmos a representao interna da data (por exemplo, ela passar
a ser representada por uma string), somente ser necessrio mudar o
cdigo de suas funes membro, sem mudar a interface da classe.
Qualquer outra parte do programa que utilizar objetos do tipo Date
no ser afetada pela mudana!!
Um programador que vai utilizar a Classe Date precisa apenas
examinar a declarao das funes membro pblicas para aprender a
utilizar a classe.
3.1.3. Construtores e Destrutores:
Como inicializar classes???? Em C, pode-se efetuar a inicializao na
declarao de uma varivel: int x = 10;
O uso de funes como Date::init(int,int,int) no a forma mais
adequada de se efetuar a inicializao, pois alm de no ser elegante,
sujeita a erro --> um programador utilizando a classe pode
simplesmente se esquecer de chamar a funo, ou ento fazer a
chamada mais de uma vez ...
Construtores: So funes (mtodos) que tem o propsito explcito de
inicializar objetos.
Tm o mesmo nome da classe, no retornam nada, tm chamada
automtica, podem ser sobrecarregadas
Quando uma classe possui um ou mais construtores, todos os objetos
daquela classe sero inicializados. Se um construtor tiver argumentos,
estes argumentos devem ser fornecidos.
class Ponto {
private:
int x,y;
public:
Ponto ( ) {x=y=0;}
Ponto(int,int);
...
};
Ponto::Ponto(int x1,int y1){
x=x1;
y=y1;
}
Ponto A, B(3,4);
Ponto *p= new Ponto(3,4);

Classes em C++ Pg. 6


Ponto *p1 = new Ponto;
Ponto Vpoints[10];
O construtor chamado para vetores o construtor sem parmetros;
Quando uma classe no possui construtores, o compilador cria um
construtor sem cdigo e sem parmetros que chamado a cada
declarao de objetos. Quando criamos um construtor para a classe,
esse construtor default desaparece!
Se criarmos um nico construtor com parmetros, s poderemos
declarar objetos com parmetros.

Ex: Classe Ponto acima, se eliminarmos o construtor Ponto( ) .


class Ponto {
private:
int x,y;
public:
Ponto(int,int);
...
};
Ponto p1;

// ERRO!

Problema poderia ser solucionado com parmetros default:


class Ponto {
private:
int x,y;
public:
Ponto(int=0, int=0);
...
};
Exemplo:
class Date {
int d, m, y;
public:
Date( int dd=0, int mm=0, int yy=0); // default = hoje
// ....
};

Classes em C++ Pg. 7

Date :: Date(int dd, int mm, int yy)


{
d = dd ? dd : today.day;
m = mm ? mm : today.month;
y = yy ? yy : today.year;
// OBS: felizmente no existe o
//ano zero no nosso calendrio
// Verifica se a data vlida
}
Problema de projeto desta classe: ela dependente de uma varivel
global, today, s podendo ser utilizada em um contexto em que today
esteja definida! Veremos, posteriormente, como resolver o problema,
usando atributos de classe.
Construtor de Cpia: Para inicializar um objeto com os valores
guardados em outro objeto da mesma classe j existente.
Ponto(const Ponto &); // Prottipo
Ponto :: Ponto (const Ponto& pt_copiado) {
x = pt_copiado.x;
y = pt_copiado.y;
}

// Definio

Ponto A(3,4), B;
Ponto C=A;
void f(Ponto pt);
Ponto D;
f(D);

// Construtor de cpia chamado aqui.

Por qu o construtor de cpia tem que receber uma referncia? Se no


fosse referncia, estaramos em uma recurso infinita!
Destrutores:
Destrutor: Funo complementar s funes construtoras de uma
classe. Sempre que o escopo de um objeto encerra-se, esta funo
chamada.

Classes em C++ Pg. 8


Os destrutores podem ser muito teis no caso de objetos com atributos
dinmicos, onde nos construtores se faz a alocao dinmica de
memria, nos destrutores deve ser feita a liberao desta memria.
Cada classe pode ter somente um destrutor que jamais recebe
parmetros. O destrutor tambm no tem nenhum tipo de retorno.
O nome do destrutor ~Nome_da_classe;
class Ponto {
private:
int x,y;
public:
Ponto(int=0, int=0);
~Ponto();
...
};
Ponto :: ~Ponto () {
// Cdigo do destrutor
}
Se os objetos so globais, os destrutores so chamados de forma
implcita quando seu escopo global deixa de existir. A ordem que o
processador segue ao chamar o destrutor implicitamente para cada
objeto inversa ordem de chamada das funes construtoras: o
primeiro objeto a ser criado o ltimo a ser destrudo.
Chamada de construtores no default em matrizes: deve ser feita de
forma explcita:
Ponto pts[2] = { Ponto(1,2), Ponto(5,7) }
Destrutor em matrizes: a destruio segue a ordem inversa da criao:
ltimo objeto da matriz o primeiro a acionar o destrutor.
3.1.4. Ponteiro this (auto-referncia).
Cada um dos objetos de uma determinada classe tem sua cpia dos
membros atributos de maneira automtica;
As funes membro tm o cdigo compartilhado por todos os objetos
da classe;
Como o programa sabe, dentro do cdigo da funo, qual o objeto
que est sendo acessado?

Classes em C++ Pg. 9


Para efetivar a ligao entre funo membro e objeto o C++ possui um
ponteiro implicitamente criado para cada funo da classe,
denominado this. Sua finalidade apontar para o objeto que chamou a
funo.
#include <iostream.h>
#include <string.h>
class Pessoa {
char nome[50];
void teste( ) { cout << "\n Teste de " << nome;}
public :
Pessoa(char * n = " ") {strcpy(nome,n);}
void saida();
};
void Pessoa :: saida()
{
cout << "\n Nome: " << nome << " de novo " << this->nome;
this->teste();
teste();
}
int main() {
Pessoa fulano("Joao"), ciclano("Maria"), semnome;
fulano.saida(); ciclano.saida(); semnome.saida();
return 0;
}
No exemplo da classe Date, as funes add_year(), add_month() e
add_day() foram definidas sem retornar valores. Para este conjunto de
funes interessante retornar uma referncia para o objeto que
sofreu alterao, de modo que as operaes possam ser encadeadas.
Por exemplo, gostaramos de poder escrever:
Date dt;
dt.add_day(4).add_month(2).add_year(1);
Para fazer isto, cada funo deve retornar uma referncia para um Date:
class Date {
int d, m, y;
public:

Classes em C++ Pg. 10


Date& add_year(int n);
Date& add_month(int n);
Date& add_day(int n);
// ....

// Adiciona n anos
// Adiciona n meses
// Adiciona n dias
// Outros mtodos

};
cada funo usaria o contedo do ponteiro this para fazer o retorno
Date& Date :: add_year(int n)
{
if (d==29 && m==2 && !leapyear (y+n)
{
// Cuida do 29/02
d = 1;
m = 3;
}
y +=n;
return *this;
}
3.1.5. Classes de armazenamento.
Objetos estticos: Um objeto pode ser declarado como esttico em
uma funo. Assim, sua durao em memria se prolonga at o
encerramento do escopo da funo em que ele uma varivel local
exatamente como variveis estticas!
Atributos estticos: servem para implementar o conceito de Atributo
de Classe. (pg 3, cap. 2), isto , deseja-se que todos os objetos de
uma determinada classe compartilhem de um certo dado. Faz-se isto
declarando um atributo de uma classe como esttico:
class Ponto{
int x, y;
static int cont;
// ....
};
Ponto Ponto_1, Ponto_2;
Ponto_1

Ponto_2

x
y
cont

x
y
cont

Classes em C++ Pg. 11

O atributo esttico pode ser acessado por funes membro ou por


outras partes do programa, quando pblico. Ele pode, mesmo, ser
acessado antes da existncia de qualquer objeto da classe, j que o
espao de memria que ele ocupa reservado antes da criao de
qualquer objeto.
Todo membro esttico deve ser redeclarado fora da classe, porm
dentro do escopo do arquivo da classe. Neste ponto ele pode ser
inicializado. No se pode inicializar um membro esttico dentro da
definio da classe!
class Ponto{
int x, y;
static int cont;
// ....
};
int Ponto :: cont = 7;
Membros estticos podem ser constantes: seu valor inicializado em
sua declarao fora da classe.
Funes membro estticas: So um tipo de funo membro especial
cujas principais caractersticas so:
(i)
(ii)

poder ser chamada sem estar associada a um objeto da classe;


no poder manipular membros no estticos da classe (no
possuem ponteiro this) Normalmente criadas para manipular os
membros estticos de uma classe!

#include <iostream.h>
class Ponto{
int x, y;
static int cont;
public:
Ponto( ){ cont ++;}
~Ponto();
static void mostra () { cout << cont << " ";}
};
int Ponto::cont =0;

// Declarao fora da classe

Classes em C++ Pg. 12

int main() {
Ponto :: mostra();
Ponto p1;
p1.mostra();

// Chamada sem objeto declarado

{ Ponto p2;
p2.mostra();
}
Ponto p2;
p2.mostra();
return 0;
}
Ponto :: ~Ponto () {
cout << "Destruindo Ponto numero" << cont << " ";
cont --;
}
Resultados:
0 1 2 Destruindo Ponto numero2 2 Destruindo Ponto
numero2 Destruindo Ponto numero1
Resolvendo o problema do construtor da classe Date .(pgs 6 e 7):
eliminando a varivel global:
#include <iostream>
class Date {
int d, m, y;
static Date default_date;
public:
Date( int dd=0, int mm=0, int yy=0); // default = default_date
// ....
static void set_default (int, int, int);
void show();
};
Date :: Date(int dd, int mm, int yy)
{
d = dd ? dd : default_date.d;
m = mm ? mm : default_date.m;
y = yy ? yy : default_date.y;

Classes em C++ Pg. 13

// Verifica se a data vlida


}
void Date :: show(){
cout << " Dia " << d << " Mes " << m << " Ano " << y ;
}

// Definicao do atributo e funo static


Date Date::default_date(16, 12, 1770);
void Date :: set_default (int m, int d, int y)
{
Date::default_date = Date(m, d, y);
}
void main()
{
Date beethoven, hoje(3,12,1998);
beethoven.show();
hoje.show();
}
Funes membro constantes:
Date no possui nenhuma funo para mostrar seus atributos. Por isto,
tivemos que "improvisar" a funo show() no programa anterior.
Vamos adicion-las:
class Date {
int d, m, y;
// ...
public:
int day( ) const { return d; }
int month ( ) const { return m; }
int year ( ) const ;
// ...
};
int Date :: year ( ) const

// No se esquecer de colocar const !

Classes em C++ Pg. 14


{
return y;
}
const colocado aps a lista de parmetros indica que a funo no
modifica o estado de Date
O compilador detecta qualquer tentativa acidental de violar esta
premissa.
Uma funo membro const pode ser chamada para objetos const e no
const. Uma funo membro no const somente pode ser chamada para
um objeto no const!
void f( Date &d, const Date& cd)
{
int i = d.year();
// OK
d.add_year(1);
// OK
int j = cd.year( );
cd.add_year(1);

// OK
// erro: cd no pode ser alterado

}
Fsicamente e lgicamente constantes: atributos mutable.
Ocasionalmente, uma funo membro logicamente constante, mas
ela ainda necessita modificar o valor de um membro de um objeto.
Para o cliente a funo parece no modificar o estado do objeto,
porm algum detalhe que o cliente do objeto no observa diretamente
modificado. Isto chamado de "funo logicamente constante".
Por exemplo, a classe Date poderia possuir uma funo retornando
uma string representando a data. Construir esta representao pode ser
uma operao cara. Portanto, pode fazer sentido manter uma cpia de
forma que requisies repetidas simplesmente retornariam esta cpia,
a no ser que os valores de Date tenham sido modificados. Valores
colocados num "cache" como estes so mais comuns em estruturas de
dados mais complexas, mas pode ser adicionado em Date da forma
seguinte:
class Date{
bool cache_valid;
string cache; // string definida na biblioteca padro do C++
void compute_cache_value( );
// ...

Classes em C++ Pg. 15


public:
// ...
string string_rep( ) const; // Representa a data como string
// No pode modificar cache!
};
Sob o ponto de vista do cliente, string_rep no deve mudar o estado de
Date, portanto deve, claramente, ser uma funo membro constante.
Porm, o cache tem que ser modificado, antes que possa ser utilizado.
Soluo: declarar o cache como mutable:
class Date{
mutable bool cache_valid;
mutable string cache;
void compute_cache_value( );
// ...
public:
// ...
string string_rep( ) const; // Representa a data como string
};
string Date :: string_rep( ) const
{
if (!cache_valid)
{
compute_cache_value( );
cache_valid = true;
}
return cache;
}
Declarar um membro de uma classe como mutable apropriado
quando somente parte da representao pode sofrer modificao,
como no caso acima
3.2. Objetos e atributos dinmicos.

A linguagem C++ oferece uma sintaxe poderosa para o gerenciamento


de objetos e atributos dinmicos.

Classes em C++ Pg. 16


3.2.1. Atributos dinmicos:
A alocao de atributos dinmicos ocorre quando algum membro da
classe um ponteiro e alocamos espao para o mesmo quando um
objeto criado. Isto normalmente feito pelos construtores da classe
Quando termina o escopo dos objetos, deve-se desalocar a memria
alocada para os atributos dinmicos usando-se para isto o destrutor da
classe.
Exemplo:
#include <string.h>
#include <iostream.h>
const MAX = 1000;
class Empresa {
char * nome;
long empregados;
double faturamento, * salarios;
void folha();
public :
Empresa();
void saida();
~Empresa();
};
int main()
{
Empresa firma;
firma.saida();
return 0;
}
Empresa :: Empresa ()
{
char inbuf[MAX];
cout << "\nDADOS DA EMPRESA : \n" <<"Nome da Empresa:";
cin.getline(inbuf,MAX);
cout << "Numero de Empregados : ";
cin >> empregados;
cout << "Faturamento
: ";

Classes em C++ Pg. 17


cin >> faturamento;
nome = new char[strlen(inbuf)+1];
salarios = new double[empregados];
if(!nome || !salarios) {
cout << "Memoria Insuficiente";
exit(0);
}
strcpy(nome,inbuf);
folha();
}
void Empresa :: saida ()
{
double resultado = faturamento;
for (int i = 0; i < empregados; i++)
resultado -= salarios[i];
cout << "\n\n\nCADASTRO DA EMPRESA : "
<< "\n\tNome
: " << nome
<< "\n\tNumero de Empregados : " << empregados
<< "\n\tFaturamento
: " << faturamento
<< "\n\tResultado no Periodo : " << resultado;
}
void Empresa :: folha()
{
cout << "ENTRE COM OS SALARIOS : \n\n";
for (int i = 0; i < empregados; i++) {
cout << (i+1) << (char) 167 << " Funcionario : ";
cin >> salarios[i];
}
}
Empresa :: ~Empresa()
{
delete [ ] nome;
delete [ ] salarios;
}
3.2.2. Passagem de Objetos por Referncia
Sempre que um objeto passado (por valor) como parmetro para uma
funo, torna-se uma varivel local da funo. O construtor de cpia

Classes em C++ Pg. 18


chamado, fazendo uma cpia para o objeto local. Ao final da funo,
termina o escopo da cpia e ela destruida.
Se o objeto passado funo possuir atributos dinmicos liberados no
destrutor, a cpia do objeto liberar esta regio de memria e,
consequentemente, tambm a regio de memria que armazenava os
atributos dinmicos PROBLEMAS!!!!!!!!!!!!!
Soluo: Passagem por referncia!!
Exemplo com problemas:
#include <iostream>
class Student {
char* name;
int nota;
public:
Student(char *nome = " ", int n = 0);
~Student();
void mostra_nome();
};
Student :: Student(char *nome, int n)
{
int tam = strlen(nome);
name = new char[tam];
strcpy( name, nome);
nota = n;
}
Student :: ~Student()
{
delete [] name;
}
void Student :: mostra_nome( )
{
cout << name;
}
void f( Student st1) { }

// O correto seria por referncia


// void f( Student& st1) { }

Classes em C++ Pg. 19


void main( )
{
Student est1, est2("Renato", 10);
f (est2); // OOOPS, perde o atributo dinmico ...
est2.mostra_nome(); // Lixo
}
O mesmo problema ocorre quando se copia objetos com o operador de
atribuio (=). Cada membro do objeto da direita copiado no membro
respectivo do objeto da esquerda. Sempre que o membro for um
ponteiro, teremos dois ponteiros apontando para o mesmo endereo de
memria Quando um dos objetos destrudo, o destrutor libera a
regio de memria e o segundo objeto fica com um membro apontando
para uma regio de memria j liberada!
Soluo: Sobrecarregar o operador de atribuio (Captulo 13 do
Livro)
3.2.3. Criao de objetos dinmicos
Nome_Classe *ptr = new Nome_Classe
Empresa *ptempresa = new Empresa;
ptempresa->saida();
Nome_Classe *ptr = new Nome_Classe[tamanho];
Empresa *arrayEmpresa = new Empresa[20];
OBS: O construtor usado para matrizes de objetos dinmicos o
construtor default. No d para usar inicializao, como fazamos em
matrizes de objetos estticos!!!!
A destruio de objetos dinmicos feita usando o operador delete:
delete ptempresa;
delete [] arrayEmpresa;
destrutor da classe ser chamado para cada objeto Empresa do
arrayEmpresa!
Programa objsdin.cpp, pgina 196.

Classes em C++ Pg. 20

3.3. Friends
Friends: so classes ou funes que podem acessar os membros privados
de uma outra classe!
3.3.1. Funes friends
Quando se declara uma funo como membro de uma classe, estamos
dizendo trs coisas distintas:
1. Que a funo pode acessar a parte privada da classe (a parte
pblica ela j poderia acessar sem problemas);
2. Que a funo est no escopo da classe;
3. Que a funo deve ser chamada em um objeto (tem um ponteiro
this).
Ao declarar uma funo como friend, damos a ela a primeira
propriedade apenas, isto , uma classe permite que uma funo
friend acesse seus membros privados, quando a declara dentro de
sua definio.
Sintaxe:
class Nome_da_Classe {
...
tipo_acesso :
...
friend prottipo_da_funo_friend;
};
A funo friend no pertence classe e, portanto, no tem qualquer
declarao de acesso.
a prpria classe quem indica suas funes friends.
As funes friends podem ser membros de uma outra classe ou nomembros de classes.
Funes friend no-membros de classe:
Funes genricas de manipulao de dados s quais queremos enviar
os atributos privados de uma determinada classe.
Exemplo: Uma funo que precisa acessar os atributos privados de
duas classes diferentes, precisa ser friend de ambas:
class Linha;

Classes em C++ Pg. 21


class Caixa{
int cor;
int upx, upy;
int lowx, lowy;
public:
friend int mesma_cor(Linha l, Caixa c);
...
};
class Linha{
int cor;
int comecox, comecoy;
int finalx, finaly;
public:
friend int mesma_cor(Linha l, Caixa c);
...
};
int mesma_cor(Linha l, Caixa c) {
if(l.cor == c.cor) return 1;
return 0;
}
Funes friend membros de outra classe:
class ClasseA {
...
tipo_acesso :
...
friend ClasseB :: prottipo_da_funo_friend;
};
Exemplo: OBS: conexo de ocorrncia entre Caixa e Funcionrio.
class Funcionario;
class Caixa {
double disponivel, folha;
public :
void fechamento(Funcionario *, unsigned);
};
class Funcionario {
double salario;

Caixa

Funcionario
salario

fechament
o

1
1,m

Classes em C++ Pg. 22


public:
Funcionario();
friend void Caixa ::fechamento(Funcionario *,unsigned );
};
void Caixa :: fechamento(Funcionario * empregados, unsigned
total) {
for (int i = 0; i < total; i++)
folha += empregados[i].salario;
}
3.3.2. Classes Friend:
Se todas as funes da classe A devem ter acesso aos membros privados
da classe B, ento A deve ser friend de B.
class A { ... };
class B {
...
friend A;
};
A ter acesso aos membros de B, mas B no ter acesso aos membros
de A!!!!!!!!
Exemplo: Programa clfriend.cpp, pgina 210 do livro;
OBS:
Se A friend de B A pode acessar membros de B;
Se B friend de C B pode acessar membros de C;
Ento A friend de C???? NO: friends no so transitivos!!!!!!!!!
3.3.3. Crticas utilizao de friends:
Este tipo de declarao diminui as caractersticas de encapsulamento
da POO. Um programa Orientado a Objetos que usa friends em
demasia muito provavelmente passvel de uma anlise mais
eficiente!
Quando estudarmos herana, verificaremos que uma forma de se evitar
a declarao de friends se derivar as classes que precisam partilhar
atributos a partir de uma classe comum, usando-a para acessar os
membros necessrios;
Em alguns casos, porm, o uso de friends pode facilitar a
implementao de certas estruturas, como as conexes de ocorrncia

Classes em C++ Pg. 23

3.4. Sobrecarga de operadores:


3.4.1. O que sobrecarregar um operador?
Sobrecarga de funes: funes com o mesmo nome, mas com
diferentes parmetros (e cdigos diferentes);
Sobrecarga de operadores: Operadores com o mesmo nome, mas com
diferentes operandos (e cdigos diferentes);
Exemplo:
class Complex{
double re;
double im;
}
Complex A,B,C;
Seria interessante poder redefinir o operador + de modo que
C = A+B;
pudesse ser escrito para a classe Complex!
Sobrecarga de operadores!!!!!
class Complex{
double re;
double im;
public :
Complex (double r = 0, double i = 0) { re = r; im =i;}
Complex operator+ (Complex);
Complex operator* (Complex);
}
Complex a(1, 3.), b(1.2, 2);
Complex c = b;
a = b+c;
b = b + c*a; // Observe as regras de precedncia
c = a*b + Complex(1,2);

Classes em C++ Pg. 24


Operadores sobrecarregveis em C++: apenas aqueles j existentes na
linguagem:
+
|
-=
<<
>=
->

~
*=
>>
&&
[]

*
!
/=
>>=
||
()

/
=
%=
<<=
++
new

%
<
^=
==
-new[]

^
>
&=
!=
->*
delete

&
+=
|=
<=
,
delete[]

Os operadores seguintes no podem ser sobrecarregados:


::
Resoluo de escopo;
.
Seleo de membro;
.*
Seleo de membro atravs de um ponteiro para funo
O operador sobrecarregado no pode alterar as regras de precedncia e
associatividade do C e C++;
Ao menos um dos parmetros do operador dever ser membro de uma
classe.
OBS: Operadores ->* e .* acesso a membro.
#include <iostream>
class A {
public:
void fa(int i) { cout << "\n fa " << i;}
};
void main()
{
A a;
A *pa = new A;
void (A :: *s)(int);
s = A::fa;
pa -> fa(1);
(pa ->*s)(2);
a.fa(3);
(a.*s)(4);

// Ponteiro para uma funo


// membro de A
// Chamada direta
// Chamada pelo ponteiro
// Chamada direta
// Chamada pelo ponteiro

}
3.4.2. Sintaxes para a sobrecarga de operadores:

Classes em C++ Pg. 25

Um operador uma funo com um nome especial: operator op, onde


op o operador. Exemplo: Complex operator+ (Complex);

Sobrecarga como funo membro no static:


(declarao):
class Nome_classe{
...
tipo_retorno operator op (lista_de_parmetros);
};
(definio):
tipo_retorno Nome_classe::operator op (lista_de_parmetros) {
... // cdigo da funo
}
Como funo friend no membro:
class Nome_classe {
...
friend tipo_retorno operator op (lista_de_parmetros);
};
(definio):
tipo_retorno operator op(lista_de_parmetros) {
... // cdigo da funo
}
Como funo no membro e no friend:
class Nome_classe {
...
};
tipo_retorno operator op (lista_de_parmetros);
(definio):
tipo_retorno operator op(lista_de_parmetros) {
... // cdigo da funo
}
O nmero de parmetros do operador sobrecarregado depende do tipo
do operador e da funo:
Operador unrio, funo membro Nenhum parmetro
Operador unrio, funo no membro Um parmetro

Classes em C++ Pg. 26


Operador binrio, funo membro Um parmetro
Operador binrio, funo no membro Dois parmetros
Exemplo: classe Complexo:
class Complexo {
double real, imaginario;
public:
Complexo (double = 0, double = 0); //Construtor
Complexo operator+ (const Complexo&); // Como membro
Complexo& operator++ (); // Membro, pr-fixado
friend Complexo operator- (const Complexo&, const Complexo&
); //Friend
friend Complexo& operator-- (Complexo&); // Friend, pr-fixado
};
Complexo Complexo :: operator+ (const Complexo& b) {
Complexo aux;
aux.real = real + b.real;
aux.imaginario = imaginario + b.imaginario;
return aux;
}
Complexo operator- (const Complexo &a, const Complexo &b) {
Complexo aux;
aux.real = a.real - b.real;
aux.imaginario = a.imaginario - b.imaginario;
return aux;
}
Complexo& Complexo :: operator++ () {
real +=1;
imaginario +=1;
return *this;
}
Complexo& operator-- (Complexo &a) {
a.real -=1;
a.imaginario -= 1;
return a;
}
Complexo a(1.,2.), b(3., 2.), c;

// pr-fixado

// pr-fixado

Classes em C++ Pg. 27


c = a+b;
c = a-b;
++c;
-- c;

// c = a.operator+(b)
// c = operator-(a,b);
// c.operator++()
// operator--(c);

Por que a passagem por referncia?


Por que o retorno por referncia nos operadores ++ e --?
Por que o retorno por valor nos operadores + e -?
Deve-se sempre pensar que um operador poder fazer parte de uma
expresso complicada, envolvendo vrios operadores: Exemplo:
a+b+c-d ;
Passagem e retorno por referncia sempre que for possvel!!!
Exemplo:
class X {
public:
// Membros (com ponteiro this implicito)
X* operator& ( );
// & unrio, prefixado (endereco)
X operator& (const X&);
// & binrio (and)
X operator++ ( );
// ++ pre-fixado
X operator++ (int);
// ++ pos-fixado
X operator& (X, X) ;
// Erro! Ternrio!
X operator/ ( );
// Erro! / Unrio?
};
// No membros
X operator- (X);
X operator- (X, X);
X operator- (X, X, X);
X operator- ( );
X operator-- (X&, int);
X operator-- (X&);
X operator% (X);

// - Unrio, pr-fixado
// - Binrio
// Erro! Ternrio
// Erro! Sem operando!
// Unrio, ps-fixado
// Unrio, pr-fixado
// Erro! % Unrio?

Uma funo definindo um operador tem que ser uma funo membro ou
receber ao menos um argumento de um tipo definido pelo programador.
Uma operador recebendo um tipo bsico como primeiro argumento no
pode ser uma funo membro.
Exemplo:

Classes em C++ Pg. 28


class Complexo {
Complexo operator+ (int);
...
};

// Complexo + int

Complexo aa, bb;


int i;
bb = aa + i; // OK, Chamaria aa.operator+(i);
bb = i + aa; // Erro! Teria que chamar i.operator+(aa)
Problema facilmente resolvvel com friends ou com funes no membro
da classe.
Exemplo: uma classe Complex, mais completa
Gostaramos de poder escrever:
Complex a = Complex(1, 2);
Complex b = 3;
Complex c = a+2.3;
Complex d = 2 + b;
Complex e = -b-c;
b = c*2*c;
if (e == c) cout << c;
Linha bsica do projeto dos operadores: minimizar o acesso
representao interna da classe, isto , restringir o nmero de operadores
que necessitam ser membros da classe ou friends dela.
class Complex {
double re, im;
public:
Complex( double r = 0., double i=0.) { re = r; im = i;}
double real( ) const { return re; }
double imag( ) const { return im;}
Complex& operator+= (const Complex& a) {
re +=a.re;
im +=a.im;
return *this;
}
Complex& operator+= (double a) {

Classes em C++ Pg. 29


re += a;
return *this;
}
//

-=, *= e /=

};
Complex operator+(const Complex& a, const Complex& b)
{
Complex r = a;
return r +=b; // Acessa a representacao por +=
}
Complex operator+(double a, const Complex& b)
{
Complex r = b;
return r +=a; // Chama Complex::operator+=(double)
}
Complex operator+(const Complex& a, double b)
{
Complex r = a;
return r +=b; // Chama Complex::operator+=(double)
}
OBS: os dois operadores anteriores no so necessrios, pois atravs
do construtor de Complex pode haver converso implcita de double
para Complex!
// -, * e /
inline bool operator== (const Complex& a, const Complex& b)
{
return ( a.real( ) == b.real( ) && a.imag( ) == b.imag( ));
}
// !=
Ento:
Complex x(1,2), y(3,4), z(2,1);
Complex r1 = x+y+z; // r1=Complex+(Complex+(x,y),z)
Complex r2 = x;
r2 +=y;
// r2.operator+=(y)
r2 +=z;
// r2.operator+=(z)
double b = 3.;
r1 = b;
// r1 = Complex(b)

Classes em C++ Pg. 30


r2+=b;
if (r2 == r1) ... ;
if( b == r1) ... ;
Exemplo: operador friend de duas classes
class Matrix;
class Vector {
float *v;
int lines;
public:
Vector (int lin);
// ...
Vector operator= (const Vector&);
// Precisa ser sobrec.
friend Vector operator* (const Matrix&, const Vector&);
};
class Matrix {
float **m;
int lines, columns;
public:
Matrix(int lin, int col);
friend Vector operator* (const Matrix&, const Vector&);
};
Vector operator* (const Matrix& mat, const Vector& vec)
{
Vector r(mat.lines);
for (int i = 0; i < mat.lines; i++) {
r.v[i] = 0.;
for (int j=0; j<mat.columns; j++) r.v[i] += mat.m[i][j]*vec.v[j];
}
return r;
}

Classes em C++ Pg. 31


3.4.3. Sobrecarga do operador de atribuio
s operadores = (atribuio) e & (endereo) tm significado prdefinido, mas podem ser alterados. O operador de atribuio (=)
deve ser sobrecarregado sempre que seu comportamento for diferente
do operador de atribuio default criado pelo C++ (exemplo, classe
Vector no programa do final da seo anterior);
Exemplo:
#include <string.h>
#include <stdlib.h>
#include <iostream.h>
const MAX = 120;
class String {
char * msg;
void aloca(char*);
public :
String(char* = "");
~String();
void mostrar();
String& operator =(char *);
String& operator =(String&);
};
String :: String (char * str) {
msg = 0;
aloca(str);
}
String& String :: operator = (char* str) {
aloca(str);
return *this;
}
String& String :: operator = (String& str) {
aloca(str.msg);
return *this;
}

Classes em C++ Pg. 32


void String :: aloca (char* str) {
if (msg)
delete [] msg;
msg = new char[strlen(str)+1];
if (!msg) {
cout << "\nMemoria Insuficiente!!";
exit(1);
}
strcpy(msg,str);
}
void String :: mostrar() {
cout << msg;
}
String :: ~String() {
delete [] msg;
}
int main() {
String texto("Sobrecarga da Atribuicao"), copia("Inicio");
copia = texto;
cout << "\ntexto : "; texto.mostrar();
cout << "\ncopia : "; copia.mostrar();
cout << "\n\nEntre com uma Mensagem : ";
char inbuf[MAX];
cin.getline(inbuf,MAX);
texto = inbuf;
cout << "\ntexto : "; texto.mostrar();
cout << "\ncopia : "; copia.mostrar();
return 0;
}
O operador tambm foi sobrecarregado para se poder atribuir a um
objeto da classe String uma string.
Por que fazer o retorno de uma referncia para *this? Permitir que
expresses a=b=c=d possam ser contrudas!
Se no tivssemos sobrecarregado o operador de atribuio, teramos
ambas variveis (copia e texto) com seu atributo msg apontando para a
mesma regio de memria!
O construtor de cpia (diferente do default) tambm deve ser criado
para esta classe!
Observao: Esta implementao daria problemas se fizssemos:
texto=texto;
Soluo:

Classes em C++ Pg. 33

String& String :: operator = (String& str) {


if(this == &str) return *this;
aloca(str.msg);
return *this;
}
3.4.4. Sobrecarga do operador de fluxo:
Podemos sobrecarregar os operadores >> e << para fazer a leitura e
impresso dos objetos das classes que definirmos.
Exemplo: Classe String anterior:
#include <string.h>
#include <stdlib.h>
#include <iostream.h>
const MAX = 120;
class String {
char * msg;
void aloca(char *&);
public :
String();
~String();
friend istream& operator >> (istream&, String&);
friend ostream& operator << (ostream&, String&);
};
int main() {
String texto;
cin >> texto;
cout << "\nFrase Final : " << texto;
return 0;
}
String :: String () {
msg = NULL;
cout <<"\nEntre com uma frase : ";
char inbuf[MAX]; cin.getline(inbuf,MAX);

Classes em C++ Pg. 34


aloca(inbuf);
}
istream& operator >> (istream& op, String& str) {
cout << "\nModificacao de
: " << str.msg
<< "\nNova String a Guardar : ";
char inbuf[MAX]; cin.getline(inbuf,MAX);
str.aloca(inbuf);
return op;
}
ostream& operator << (ostream& op, String& str) {
cout << str.msg;
return op;
}
String :: ~String() {
delete [] msg;
}
OBS: As sobrecargas dos operadores de fluxo devem ser friends da
classe, pois o operando da esquerda sempre um objeto das classes de
gerenciamento de I/O do C++ (Ostream e Istream)
3.4.4. Sobrecarga de new e delete:
Por que algum poderia querer sobrecarregar new ou delete? questes
de eficincia: as verses padro de new e delete so perfeitamente
adequadas para uso geral, mas sua ampla aplicabilidade
inevitavelmente deixa espao para melhorias em velocidade e
utilizao de memria. Isto especialmente verdadeiro em aplicaes
que alocam dinamicamente um grande nmero de objetos pequenos.
Os operadores new e delete precisam estabelecer uma comunicao
entre si, isto , delete, ao desalocar memria precisa saber exatamente
quanta memria foi alocada por new. A forma mais comum de new
"contar" para delete quanta memria foi alocada para um determinado
objeto adicionar memria alocada para um determinado objeto
alguns bytes a mais contendo a informao sobre quanto foi alocado.
Ou seja, ao declarar:
Funcionario *pf = new Funcionario;

Classes em C++ Pg. 35


o retorno de new seria algo como:

pf

Dados sobre a quantidade de memria alocada

Memria alocada para o objeto Funcionrio

Ento, se voc redefinir o operador new para uma determinada classe,


no intuito de aproveitar esta memria, dever tambm redefinir o
operador delete correspondente para que ele saiba como se comportar,
pois seu comportamento no ser mais o padro.
Quando se faz a sobrecarga do operador new importante que o
operador oferea um comportamento consistente com o new oferecido
pelo sistema, ou seja:
1. retorne um ponteiro do tipo correto;
2. chame uma funo de tratamento de erro quando no existe
memria suficiente e retorne 0 ( OOPS, pela nova norma, na
realidade, lana uma exceo, conforme especificado na
norma);
No vamos estudar como efetuar esta sobrecarga (ver Stroustrup), mas
importante saber que podemos estipular qual funo new vai chamar
quando no conseguir alocar memria. Isto feito chamando a funo
set_new_handler:
extern void (*set_new_handler (void (*) ( ) ) ) ( );
set_new_handler uma funo que recebe como parmetro um ponteiro
para uma funo void sem parmetros e retorna um ponteiro para uma
funo void tambm sem parmetros. O argumento a funo que new
deve chamar caso no tenha sucesso e o retorno a funo que estava
setada antes de set_new_handler ter sido chamada.
#include <iostream.h>
#include <new.h>
#include <stdlib.h> // Para o abort()

Classes em C++ Pg. 36


// Funcao a ser chamada caso o operador new falhe;
void no_mem( )
{
cerr << "O operador new falhou: acabou a memoria \n";
abort(); // Pela nova norma, substituir por throw bad_alloc();
}
int main( )
{
set_new_handler( no_mem);
char *big = new char[1000000000];
cout << "Fim \n";
// Nao vai ser executado,
// se houver problemas no new
}
3.5. Converso de tipos:

A linguagem C permite a converso implcita de tipos:


char a; float b=1.0; a=b;
Os resultados podem ser desastrosos!
Em C++, tem-se tambm as converses implcitas para os tipos prdefinidos da linguagem. Porm, C++ acrescenta as Funes
operadoras de converso, que permitem que se programe como ser
efetuada a converso para tipos definidos pelo programador;
As funes de converso so acionadas toda vez que o compilador
espera um objeto de uma determinada classe e recebe de outra.
As converses explcitas de tipo em C++ podem ser efetuadas nos
construtores ou por funes operadoras de converso.
3.5.1. Converso com funo construtora
Pode-se usar o construtor para inicializar um objeto com dados vindos
de objetos de outra classe;
Obviamente, o construtor tem que ter acesso aos membros do objeto da
outra classe, para poder copi-los.
Sintaxe:

Classes em C++ Pg. 37


class Nome_classe {
...
public:
Nome_classe(Nome_tipo);
};
Exemplo 1:
Operador soma para a classe Complex:
Complex operator+ ( Complex, Complex);
Complex operator+ (Complex, double);
Complex operator+ (double, Complex);
Isto pode se tornar problemtico ... Imagine se tivssemos 3 alternativas
de tipo para cada argumento de cada funo ... Uma funo com 1
argumento, necessitaria de trs verses, uma com dois argumentos, de
nove verses, uma com trs argumentos, de vinte e sete verses ... Se
tivermos um construtor que transforma um double em Complex,
poderemos ter uma nica verso do operador!
OBS:
Complex z(2,4);
3 + z;
// OK: Complex(3) + z;
3.operator+=(z); // Erro: 3 no um objeto pertencente a
Complex
3 += z;
// Erro: 3 no um objeto pertencente a
Complex
Ou seja, pode-se expressar a noo de que um operador requer um
Lvalue como seu operando do lado esquerdo tornando aquele operador
um membro da classe.
Exemplo 2:
#include <iostream.h>
class Cruzeiro;
class Dolar {
float money;
static float const PARALELO;

Classes em C++ Pg. 38


public :
Dolar (float mon=0) { money = mon; } // Float em Dolar
Dolar (Cruzeiro &); //Cruzeiro em Dolar
friend ostream& operator << (ostream&, Dolar&);
};
float const Dolar :: PARALELO = 1.13;

class Cruzeiro {
double dinheiro;
public :
Cruzeiro (double valor) { dinheiro = valor; } // Float em Cruzeiro
friend Dolar :: Dolar (Cruzeiro &); // Construtor de Dolar e' friend
friend ostream& operator << (ostream&, Cruzeiro&);
};
Dolar :: Dolar (Cruzeiro & qtde) {
money = (float) qtde.dinheiro/PARALELO;
}
ostream& operator << (ostream& op, Dolar& coin) {
op << "U$ ";
op.width(10); op.precision(2);
op << coin.money << '\n';
return op;
}
ostream& operator << (ostream& op, Cruzeiro& moeda) {
op <<"R$ ";
op.width(10); op.precision(2);
op << moeda.dinheiro << '\n';
return op;
}
int main () {
Cruzeiro salario(110.00), paralelo (1.13);
Dolar usa (10.50);
cout.setf(ios::showpoint); cout.setf(ios::fixed);
cout << "1. Objeto Dolar sem Conversao : " << usa;
cout << "2. Objeto Cruzeiro sem Conversao: " << salario;
Dolar salario_dolar = salario;
cout <<"3. Conversao por Construtor: " << salario_dolar;
usa = Dolar (paralelo);

Classes em C++ Pg. 39


cout << "4. Usando Const. Implicitamente: " << usa;
usa = (Dolar) paralelo;
cout << "5. Convertido por Cast: " << usa;
usa = paralelo;
cout << "6. Convertido por var_dol = var_cruz: " << usa;
return 0;
}
Resultados:
1. Objeto Dolar sem Conversao : U$
10.50
2. Objeto Cruzeiro sem Conversao: R$ 110.00
3. Conversao por Construtor: U$
97.35
4. Usando Const. Implicitamente: U$
1.00
5. Convertido por Cast: U$
1.00
6. Convertido por var_dol = var_cruz: U$
1.00
3.5.2. Sobrecarga do operador de converso
H casos em que o uso de um construtor para efetuar a converso no
adequado, porque o construtor no pode especificar:
1. Uma converso de um tipo definido pelo usurio para um tipo bsico,
pois os tipos bsicos no so classes;
2. Uma converso de um novo tipo definido pelo usurio para uma
classe previamente definida (sem que se tenha a modificao da classe
antiga).
Nestes casos, pode-se sobrecarregar o operador de converso, para
efetuar a converso de um objeto do tipo Classe1 para um objeto do tipo
Classe2:
O operador deve ser membro de Classe1;
A sintaxe a ser usada em classe 1 : operator Classe2();
O tipo de retorno no deve ser colocado, pois ele implicitamente
igual a Classe2;
No se pode definir um operador de converso e um construtor
dedicado a efetuar a mesma converso: isto ilegal, pois gera
ambiguidade!
Exemplo:
const float PARALELO = 1.13;

Classes em C++ Pg. 40


class Cruzeiro {
double dinheiro;
public :
Cruzeiro (double valor) { dinheiro = valor; }
operator Dolar (); // Cruzeiro em Dolar via operador
operator unsigned long (); // Cruzeiro em unsigned long
friend ostream& operator << (ostream&, Cruzeiro&);
};
Cruzeiro :: operator Dolar () {
return Dolar ((float) dinheiro/PARALELO);
}
Cruzeiro :: operator unsigned long () {
return (unsigned long) dinheiro;
}
void mostrar (unsigned long var_long) {
cout <<"R$ ";
cout << var_long << '\n';
}
int main () {
Cruzeiro minimo (110.00), paralelo (1.13);
Dolar usa (10.50);
unsigned long truncado = paralelo;
Dolar minimo_dolar = minimo;
truncado = (unsigned long) minimo;
mostrar(minimo);
return 0;
}
3.5.3. Converso em sobrecarga de operadores:
Seja a sobrecarga do operador de soma para a classe Cruzeiro:
class Cruzeiro{
...
friend Cruzeiro& operator + (Cruzeiro&, double);
};
Cruzeiro a,b;

Classes em C++ Pg. 41


double c,d;
a + c; // Chama o operador com parmetros OK!
d + b; // d transformado em Cruzeiro e b em double:
// as converses precisam ser definidas!!!
Deve-se considerar a questo do desempenho do programa que usa
estas converses implcitas: sempre que o compilador deve converter
um tipo em um objeto de classe, h a gerao de objetos auxiliares, o
que reduz o desempenho do programa
quando as sobrecargas so simples e o overhead for muito grande,
pode ser prefervel declarar operadores para as duas ordens de
chamada, evitando a criao de objetos temporrios!
OBS: Quando se deve usar funes friend, funes membro ou funes
no membro?
Pergunta 1: O operador realmente necessita ter acesso representao
interna da classe, ou pode fazer acesso a esta representao indiretamente
atravs de outro operador? funo no membro! Exemplos: operador
+ binrio da classe Complex, fazendo acesso atravs do operador +=,
operador ==, fazendo acesso atravs das funes membro real() e imag(),
etc ...
Pergunta 2: Quem realmente precisa ser membro?
Construtores, destrutores e funes virtuais precisam ser membros.
Funes que devem ser chamadas sobre objetos da classe tm que ser
membros.
Exemplo:
class X {
// ...
X(int);
int m1( );
int m2( ) const;
friend int f1(X&);
friend int f2 (const X&);
friend int f3 (X);
}

Classes em C++ Pg. 42

Funes membro somente podem ser chamadas para objetos de sua


prpria classe. Nenhuma converso de tipo definida pelo usurio entrar
em ao. Logo:
99.m1( );
99.m2( );
f1(99);
no constantes
f2(99);
f3(99);

// Erro, X(99).m1( ) no usado!


// Erro, X(99).m2( ) no usado!
// Erro, converso implcita no usada em referncias
// OK, usa-se f2(X(99));
// OK, usa-se f3(X(99));

Uma operao modificando o estado de um objeto deve ser uma


funo membro ou uma funo no membro recebendo um argumento
no const por referncia. Operadores que requerem um lvalue (=, *=, ++,
etc.) so definidas mais naturalmente como membros.
Se converses implcitas de tipos so desejveis para todos os
operandos de um operador, a funo implementando o operador deve ser
no membro recebendo uma referncia const como argumento, ou um
argumento passado por valor. Se houver necessidade de acesso
representao interna para implementao, a funo deve ser definida
como friend. Se no, pode ser uma funo "normal". Exemplo:
operadores binrios +, -, *, ||, &&, etc ...
Se no houver necessidade de converso de tipo, pode-se criar os
operadores como membro ou como friend, indistintamente
3.5.4. Ambiguidades em converses:
Em alguns casos, um valor de determinado tipo pode ser construdo
de mais de uma maneira: estes casos so ilegais
Em outros casos, um valor pode ser construdo pelo uso repetido de
construtores ou operadores de converso. Isto deve ser feito por
converso explcita; somente um nvel de converso implcita legal.
Exemplo:
class X {/* ... */ X(int); X(char *); };
class Y {/* ... */ Y(int); };
class Z {/* ... */ Z(X);};
X f(X);

Classes em C++ Pg. 43


Y f(Y);
Z g(Z);
f(1);
f(X(1));
f(Y(1));

// Erro: ambguo, f(X(1)) ou f(Y(1)) ???


// OK
// OK

g("teste"); // Erro, dois nveis de converso definidas


// pelo usurio so necessrios
g(X("teste")); // OK, g(Z(X("teste"))); Z(X) implcita
g(Z("teste")); // OK, g(Z(X("teste"))); X("teste") implcita
As converses definidas pelo usurio somente sero consideradas se elas
forem necessrias para resolver uma chamada. Exemplo:
class XX { /* ... */ XX(int);};
void h(double);
void h(XX);
h(1);

// Quem chamada h(double(1)); !!!!!


3.5.5. Tornando construtores explcitos:

Por default, um construtor com um nico argumento tambm define uma


converso implcita. Para alguns tipos isto o ideal, mas para outros
pode causar srios erros.
Exemplo:
Complex z = 2;

// Inicializa z com Complex(2), OK!

class String {
char *msg;
// ....
public:
String(int n) { msg = new char[n]; }
};
String s(10);
String r = 10;

// OK, String alocada com 10 posicoes


// Idem, mas era isto mesmo que se queria?

Classes em C++ Pg. 44


String t = 'a';

// OOOPS! String com int('a') elementos!

Soluo: Construtores explcitos!


class String {
char *msg;
// ....
public:
explicit String(int n) { msg = new char[n]; }
String (const char *p);
};
void f(String);
String s1 = 'a'; // Erro, sem converso implcita
String s2(10);
// OK, String alocada com 10 posicoes
String s3 = String(10); // OK, explcito
String s4 = "Belo Horizonte";
// OK
String g( )
{
f(10);
// Erro! sem converso implcita int -> String
f(String(10));
// OK, converso explcita
f("Sabara");
// OK, converso implcita
String *p1 = new String( "Betim"); // OK, explcito
String *p2 = new String(10);
// OK, explcito
return 10;
// Erro, sem converso implcita
}

Anda mungkin juga menyukai