POO, introdução II

………..Conversões de tipo primitivos
p.e.:
long l = 10;
int i = static_cast<int>(l);

………..Conversões de tipo de objectos das classes
de um tipo qualquer num objeto da nossa classe e é feito com construtores que aceitam um só argumento do tipo da origem quando é de um objecto da nossa classe num outro tipo são feitas com operadores de conversão e estas conversões podem ser implícitas ou explícitas.

p.e.:
conversão de um outro tipo num objecto da nossa classe
class Complexo {
double a; // parte real
double b; // coeficiente da parte imaginária
public:
// construtor que admite um argumento => conversão:
// argumento -> objecto da classe
Complexo(double aa = 0, double bb = 0);
Complexo & operator+=(const Complexo & x); //operador membro
// …
};
Complexo operator+(const Complexo & x, const Complexo & y);
ostream & operator<<(ostream & saida, const Complexo & x);
assim:
Complexo(double aa = 0, double bb = 0);
pode receber nenhum argumento
um argumento //-> esté o que interessa neste caso
ou dois argumentos

conversão implícita:
Complexo b = 2; // pois o que acontece é Complexo b = Complexo(2)
conversão explicita
a = b + Complexo(2); //

………..extra: operador + e – devem ser global, recomendado, são mais abrangentes
ERRO:
a = 4 + b ,
pois neste caso equivaleria a
a = 4.operator+( b).
sendo global:

………..Construtores e conversões , Construtores explicit
p.e.:
na classe String,
String(int n)
e é feito na iniclização:
String s = ‘a’; //estávamos a reservar bytes
então podemos ter um conversor com a palavra explicit ele só vai trabalhar quando quisermos
class String{
// …
String(const char * s); // construtor conversor, conversão implícita
explicit String ( int n); // construtor conversor, conversão explicita
};

………..operador de conversão
é uma função obrigatoriamente membro
tem o nome operator
tem o tipo de destino
não tem parâmetros
não tem tipo de retorno, pois retornar o tipo de destino
const não altera o objecto
p.e.:
class Relogio {
int hora; // 0 – 23
int minuto; // 0 – 59
int segundo; // 0 – 59
public:
Relogio( int hora = 0, int minuto = 0, int segundo = 0);
operator int( )const; //serve para converter em inteiro
//…
};
e o operator surgia como:
Relogio::operator int( )const{
return segundo+60*(minuto+60*hora);
}
eficiência:
int main( ){
Relogio t(2, 10, 5);
int s = static_cast<int>(t); // conversão explicita
cout << “\n Total de segundos: ” << s;
s = t; // conversão implícita
cout << “\n Total de segundos: ” << s;
}

assim as Conversões implícitas devem ser para:
argumento com que uma função é chamada para o tipo do correspondente parâmetro com que é definida;
valor de retorno de uma função para o tipo de retorno especificado no protótipo da função;
operandos em expressões;
inicializadores.

………..Operador e Opção recomendada
resumo:
operadores unários
funções membros

=()[] operadores de conversão
têm que ser funções membros

+= -= /= *= etc (observar a diferença de “estatuto” entre o 1º e o 2º operandos)
funções membros

outros operadores binários (operandos com “estatuto” análogo)
funções globais

operadores binários em que o 1º operando não pertence à classe em causa da classe em causa
não podem ser funções membros

(construtores por cópia, operador atribuição, .. )
………..composição
a associação (relacionamento de duas classes) de objectos pode ter duas formas:
ou agregação (ter, tem, agrega p.e.: classe A tem objecto classe B, sendo que os objectos da classe B existem sempre, mesmo que A seja destruída; p.e.: turma e alunos)
ou composição (tem mas, se a classe A uca objecto da classes B, se A desaparecer, B também desaparece)

composição vs agregação
p.e.:
existe um relação de composição
se o poema for destruído, os versos são destruídos
um objecto poema tem posso exclusiva dos seus versos
class Poema {
string titulo;
string * pVersos; // ponteiro para um array dinâmico de strings
int nVersos; // obrigatório se não for char *c: numero de elementos do array dinâmico
public:
Poema(const string & t);
~Poema(); // destrutor, para libertar antes
void acrescentaVerso(const string & verso);
void eliminaVerso(int ordem);
// . . .
string getAsString()const;
};

exame: não se pode usar vectores, para usar este tipo de notação

assim surge uma situação de a = b
a atribuição não pode funcionar por omissão, a solução é usar um operador atribuição, e desta forma tudo o que a atribuição faz vai ficar bloqueado
o operador atribuição vai surgir na classe
possibilitando que os objectos sejam fisicamente independentes
public:
Poema(const string & t);
void acrescentaVerso(const string & verso);
void eliminaVerso(int ordem);
Poema & operator=(const Poema & ob);//operador atribuição, operador membro
~Poema(); // destrutor
// . . .
string getAsString()const;
};

//operador atribuição a=b
Poema& Poema::operator=(const Poema& ob) { //é o A, e é o *this, sendo B o op
if (this == &ob) { // prevenção da auto-atribuição
return *this;
}
// se os membros da atribuição forem objetos diferentes
titulo = ob.titulo;
nVersos = ob.nVersos;
// libertar a memoria dinâmica usada exclusivamente pelo primeiro membro
delete[] pVersos;
pVersos = nullptr;
// se a origem da copia for um poema sem versos …
if (ob.pVersos == nullptr || ob.nVersos == 0) {
return *this;
}
// reservar memoria dinâmica para conter uma copia exclusiva dos versos do segundo membro
pVersos = new string[ob.nVersos]; //array dinamico, reserar espaço em memória
// fazer essa copia
for (int i = 0; i < ob.nVersos; ++i) {
pVersos[i] = ob.pVersos[i];
}
return *this;
}
mesmo que a classe não tenha o construtor por cópia, ela existe sempre pois é gerada automaticamente como a atribuição.
e constrói por cópia membro a membro
neste caso copiar o ponteiro pVersos, ficando todos a apontar para a mesma zona de memória

………..construtor por cópia
análogo à atribuição

p.e.:
na classe Poema surge o construtor por cópia
class Poema {
string titulo;
string* pVersos; // ponteiro para um array dinamico de strings
int nVersos; // numero de elementos do array dinamico
public:
Poema(const string& t);
void acrescentaVerso(const string& verso);
void eliminaVerso(int ordem);
Poema& operator=(const Poema & ob);//operador atribuicao, operador membro
Poema(const Poema & ob); //construtor por cópia
~Poema(); // destrutor
// . . .
string getAsString()const;
};

e a função vem:
Poema::Poema(const Poema& ob):titulo(ob.titulo), nVersos(ob.nVersos) {
// titulo e nVersos inicializados com os mesmos valores de ob
// se a origem da copia for um poema sem versos …
if (ob.pVersos == nullptr || ob.nVersos == 0) {
pVersos = nullptr;
return;
}
// reservar memoria dinamica para onde copiar os versos de ob
pVersos = new string[ob.nVersos];
// copiar o conteudo do array dinamico
for (int i = 0; i < nVersos; ++i) {
pVersos[i] = ob.pVersos[i];
}
}

o construtor por cópia funciona:
quando se cria um objecto por cópia de outro;
quando passamos um objecto por valor a uma função (surge um novo objecto);
quando uma função retorna um objecto por valor, sendo que o que sair no return é a cópia;
assim
O construtor por cópia tem usualmente a forma
EstaClasse::EstaClasse( const EstaClasse & ob)

as diferenças entre Construtor por cópia / operador atribuição:
operador atribuição tem a prevenção da auto-atribuicao, que não aparece no construtor por cópia;
no construtor por cópia não é preciso tratar da memória velha, pois o objecto está a ser criado;
e no construtor por cópia não tem os retornos;

assim sendo podíamos escrever o construtor por cópia em função do operador atribuição:
(fazer um à custa do outro)

Poema::Poema(const Poema & ob):pVersos(nullptr){ // preparar pVersos para ser operando de delete
// preparar o objeto que esta a ser criado para ser alvo do operador atribuicao
*this = ob; // aplicar o operador atribuicao: operator=(s)
}
Poema & Poema::operator=(const Poema & ob){
if (this == &ob) {// prevencao da auto-atribuição
return *this;
}
// se os dois membros da atribuicao forem objetos diferentes
titulo = ob.titulo;
nVersos = ob.nVersos;
// libertar a memoria dinamica usada exclusivamente pelo primeiro membro
delete[] pVersos;
pVersos = nullptr;
// etc.
// . . .
}

void Poema::acrescentaVerso(const string & verso){
// reserva espaço para mais um
string * aux = new string[nVersos + 1];
// copia os versos existentes
for (int i = 0; i < nVersos; ++i) {
aux[i] = pVersos[i];
}
// o novo verso está em último lugar
aux[nVersos] = verso;
++nVersos; // incrementa o número de versos
delete[] pVersos; // liberta array dinâmico anterior
// pVersos aponta para o novo array dinamico
pVersos = aux;
}

void Poema::eliminaVerso(int ordem) {
if (ordem < 0 || ordem >= nVersos){ return; } //fora dos limites
if (nVersos == 1) {// se retirar o ultimo
delete[] pVersos;
pVersos = nullptr;
nVersos = 0;
return;
}
// reserva espaço ajustado para menos um
string * aux = new string[nVersos – 1]; //nao for o ultimo, aloco novo espaço
// copia os versos anteriores ao verso a eliminar
for (int i = 0; i < ordem; ++i) {
aux[i] = pVersos[i];
}
// copia os versos posteriores ao verso a eliminar
for (int i = ordem; i < nVersos – 1; ++i) {
aux[i] = pVersos[i + 1];
}
–nVersos; // decrementa o número de versos
delete[] pVersos; // liberta array dinâmico anterior
pVersos = aux; // pVersos aponta para o novo array dinamico
}

………..composição de objectos
um objecto dentro de outro objecto
quando o objecto for criado, antes funcionam todos os construtores dos sub-objectos. funcionam porque nos especificamos ou então é por omissao.
p.e.:
class String {
char* p;
void setString(const char * s);
public:
String(); // construtor por defeito
String(const char * s); // construtor conversor
String(const String & s); // construtor por copia
~String(); // destrutor

const char* getString()const;
ostream & imprime(ostream & saida)const;
String & operator=(const String & x); // operador atribuicao
String & operator+=(const String & x);

static int strTam(const char * s);
};
String::String() {
p = 0;
cout << “Constr. string nula” << endl;
}

String::String(const char * s) {
setString(s);
cout << “Constr. string ” << (p ? p : “nula”) << endl;
}

String::String(const String & s) {
setString(s.p);
cout << “Constr. por copia string ” << (p ? p : “nula”) << endl;
}

String::~String() {
cout << “Destr. string ” << (p ? p : “nula”) << endl;
delete[] p;
}

void String::setString(const char * s) {
int t = strTam(s);
if (!t) {
p = 0;
return;
}
p = new char[t + 1];
if (!p) {
cout << “\nMemoria insuficiente”;
return;
}
strcpy_s(p, 5 ,s);
}

const char* String::getString()const { return p; }

ostream& String::imprime(ostream & saida) const {
if (p) saida << ” ” << p;
else saida << ” String nula”;
return saida;
}

String& String::operator=(const String & x) {
if (this != &x) {
delete[] p;
setString(x.p);
}
cout << “Oper. atrib. string ” << (p ? p : “nula”) << endl;
return *this;
}
String& String::operator+=(const String & x) {
char * aux = new char[strTam(p) + strTam(x.p) + 1];
if (!aux) {
cout << “\nMemoria insuficiente”;
return *this;
}
if (p) strcpy(aux, p);
if (x.p) strcpy(aux + strTam(p), x.p);
delete[] p;
p = aux;
return *this;
}
int String::strTam(const char * s) {
return (s ? strlen(s) : 0);
}
ostream& operator<<(ostream & saida, const String & x) {
return x.imprime(saida);
}

class Data {
int dia;
int mes;
int ano;
public:
Data(int d = 1, int m = 1, int a = 2000);
~Data();
int getDia() const;
int getMes() const;
int getAno() const;
void setDia(int d);
void setMes(int m);
void setAno(int a);
void anosDepois(int n);
};
Data::Data(int d, int m, int a) {
cout << “Constr. data ” << d << “/” << m << “/” << a << endl;
dia = d; mes = m; ano = a;
}
Data::~Data() {
cout << “Destr. data ” << dia << “/” << mes << “/” << ano << endl;
}
int Data::getDia() const { return dia; }
int Data::getMes() const { return mes; }
int Data::getAno() const { return ano; }
void Data::setDia(int d) { dia = d; }
void Data::setMes(int m) { mes = m; }
void Data::setAno(int a) { ano = a; }
void Data::anosDepois(int n) { ano += n; }

class Pessoa {
String nome;
String endereco;
String telefone;
Data nascimento;
public:
Pessoa(const char* n, const char* end, const char* tel, int d, int m, int a);
Pessoa(const char* n);
~Pessoa();
int getAnoNascimento()const;
};

Pessoa::~Pessoa() {
cout << “Fim\n”;
}

Pessoa::Pessoa(const char * n, const char * end, const char * tel, int d, int m, int a) : nome(n), endereco(end), telefone(tel), nascimento(d, m, a) {
cout << “Inicio\n”;
}

Pessoa::Pessoa(const char* n) : nome(n) {
cout << “Inicio\n”;
}

int Pessoa::getAnoNascimento()const {
return nascimento.getAno();
}

int main() {
Pessoa a(“Pedro Sousa”, “Rua do La Vai Um, 1, Coimbra”, “11111”, 1, 11, 1980);
Pessoa b(“Teresa Oliveira”);
cout << “Pedro Sousa nasceu em ” << a.getAnoNascimento() << endl;
cout << “Teresa Oliveira nasceu em “<< b.getAnoNascimento() << endl;
Pessoa c = a; // construção por cópia
c = a; // atribuição

}

Tags : , , , , ,

0 thoughts on “POO, introdução II”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.