Tag: poof
excepções e vários
………..varios, exercicios excepções (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; void funcao() { double dX = -23.99; cout << "se funcao : antes excepcao " << endl; //se não houver excepções corre tudo bem //throw - 1; //sai aqui em estado de erro, lançada a execpção que não é tratata //ou //throw "um texto que e erro..";//sai aqui em estado de erro, lançada a execpção que não é tratata //ou //throw dX; //sai aqui em estado de erro, lançada a execpção que não é tratata cout << "se funcao : depois excepcao " << endl; } int main() { cout << "\n inicio do main" << endl; funcao(); cout << "\n fim do main" << endl; return 0; }
………..varios, exercicios excepções (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class minhaExcepcao { const char* const descricao; public: minhaExcepcao(const char * const msg =0):descricao(msg){} }; void funcao2() { double dX = -23.99; cout << "se funcao : antes excepcao " << endl; //esta versão também não adianta.. throw minhaExcepcao("algo aconteceu"); cout << "se funcao : depois excepcao " << endl; } int main() { cout << "\n inicio do main" << endl; funcao2(); cout << "\n fim do main" << endl; return 0; }
………..varios, exercicios excepções (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class Erro { }; void funcao3() { cout << "se funcao : antes excepcao " << endl; try { //criação artifical de um erro de uma excepção com o uso do throw //ou //sendo executado o primeiro thorw e interrompe este try throw -1; //ou throw "primeira linha de comentario"; //ou throw Erro(); //esta excepção não é tratada num catch, logo termina em erro, mesmo com a class estabelecida //ou //se nao for lançada nenhuma excepção então tbm não vai ser executado nenhum catch, e é porque está tudo bem } catch (int x) { //e é sempre executado no máximo um catch cerr << "apanho o valor int da excepcao: " << x << endl; }catch(const char * s) { cerr << "apanho o valor char da excepcao: " << s << endl; } cout << "se funcao : depois excepcao " << endl; } int main() { cout << "\n inicio do main" << endl; funcao3(); cout << "\n fim do main" << endl; return 0; }
………..varios, exercicios excepções (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; int main() { cout << "\n inicio do main" << endl; try { throw - 1; } catch (int x) { //e é sempre executado no máximo um catch cerr << "apanho o valor int da excepcao: " << x << endl; } catch (double) { cerr << "apanho o valor double da excepcao: "<< endl; } catch (const string & str) { cerr << "apanho o valor string da excepcao: " << str << endl; } cout << "\n fim do main" << endl; return 0; }
………..varios, exercicios excepções (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class denominadorNull { public: string sobre() { return string("denominador nulo\n"); } }; class foraDosLimites { public: string sobre() { return string("fora dos limites\n"); } }; double divisao(int numerador, int denominador) { if(!denominador) { throw denominadorNull(); } return static_cast<double>(numerador) / denominador; } int main() { cout << "\n inicio do main" << endl; int a[4] = { 10,11,12,13 }; int index = 1, d; while(index >=0) { cout << "qual o index: "; cin >> index; cout << "qual o denominador: "; cin >> d; try { //ou //throw "aaa"; //ou if (index < 0 || index >= 4) throw foraDosLimites(); //e interrompe, não foi resolvido nao existe um catch //se correr bem continuo cout << "resultado: " << divisao(a[index], d) << endl; } catch (denominadorNull & dn) { cerr << dn.sobre(); //depois disto considera-se resolvido } catch(foraDosLimites & fl) { cerr << fl.sobre(); //depois disto considera-se resolvido } catch (...) //apanha todos os outros erros { cerr << "qualquer outro erro\n"; //depois disto considera-se resolvido } } return 0; }
………..varios, exercicios excepções (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class baseErro { public: virtual void oque() { cout << "baseErro" << endl; } }; class derivadaErro : public baseErro { public: virtual void oque() { cout << "derivadaErro" << endl; } }; void funcao6() { throw derivadaErro(); } int main() { try { funcao6(); } catch (baseErro b) //não é usada a derivada mas sim a base, //não existe neste caso o polimorfismo { b.oque(); } try { funcao6(); } catch (baseErro & b) //convem passar por referencia para existir o polimorfismo //porque é passado por referencia, surgre assim o erro da derivada //e para que não se faça mais uma copia da excepção que foi lançada { b.oque(); } return 0; }
………..varios, exame1920_epocanormal (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class Bilhete { string passageiro; int passaporte; string & companhia; //obrigatoria ser na lista de inicialização, se for & e const vector<int> id_malas; //Acrescentar bagagens ao bilhete (representadas pelos seus ID e sem repetir): int pesquisa(int id)const; public: //ou 1 //Bilhete(string passageiro0, int passaporte0, string & companhia0, vector<int> id_malas0); //ou 2 Bilhete(string passageiro0, int passaporte0, string& companhia0, initializer_list<int> id_malas0); //lidar com a atribuir objectos b1=b2 Bilhete& operator=(const Bilhete& ob); //Acrescentar bagagens ao bilhete (representadas pelos seus ID e sem repetir): //operador que pode ser membro Bilhete& operator <<(int id); //por estar em cadeia //ou //void operator <<(int id); //se não estivesse em cadeia //Remover todas as bagagens com id superior a um indicado): Bilhete& operator -=(int id); //por estar em cadeia //extra string getAsAstring()const; }; //Mostrar o conteúdo no ecrã da forma (bilhete1 e bilhete2 são objetos da classe Bilhete): ostream& operator<<(ostream& saida, const Bilhete & ob); //Acrescentar bagagens ao bilhete (representadas pelos seus ID e sem repetir): int Bilhete::pesquisa(int id) const { for(unsigned int i = 0; i< id_malas.size(); i++) { if(id == id_malas[i]) { return i; } } return -1; } //Acrescentar bagagens ao bilhete (representadas pelos seus ID e sem repetir): Bilhete& Bilhete::operator<<(int id) { int pos = pesquisa(id); if(pos == -1) { //não está lá o id id_malas.push_back(id); } //senão não faço nada return *this; // pedido em cadeia, o retorno é *this } //Remover todas as bagagens com id superior a um indicado): Bilhete& Bilhete::operator-=(int id) { for(auto it= id_malas.begin(); it != id_malas.end(); ) { if(*it >id) { it = id_malas.erase(it); }else { ++it; } } return *this; } //ou //void Bilhete::operator<<(int id) //se não estivesse em cadeia //ou 1 //Bilhete::Bilhete(string passageiro0, int passaporte0, string& companhia0, vector<int> id_malas0) : passageiro(passageiro), //passaporte(passaporte), //companhia(companhia), //id_malas(id_malas) //{ //} //ou 2, versão "mais complicada" Bilhete::Bilhete(string passageiro0, int passaporte0, string& companhia0, initializer_list<int> id_malas0) : passageiro(passageiro0), passaporte(passaporte0), companhia(companhia0) { //para lidar com o initializer_list for(auto & m : id_malas0) { id_malas.push_back(m); } } //lidar com a atribuir objectos Bilhete& Bilhete::operator=(const Bilhete& ob) { //evitar autoatribuição if(this == &ob) { return *this; } //pode-se atribuir tudo menos a companhia, i.e. referencias e const passageiro = ob.passageiro; passaporte = ob.passaporte; id_malas = ob.id_malas; return *this; } //extra string Bilhete::getAsAstring() const { ostringstream oss; oss << "Passageiro: " << passageiro << " Passaporte: " << passaporte << " Companinha: " << companhia << "mala:" << endl; for (int i : id_malas) { oss << " " << i; } oss << endl; return oss.str(); } //Mostrar o conteúdo no ecrã da forma (bilhete1 e bilhete2 são objetos da classe Bilhete): ostream& operator<<(ostream& saida, const Bilhete& ob) { saida << ob.getAsAstring(); return saida; } int main() { //alinea 6. //string & companhia; exige uma variavel string aux_nome("tap"); //ou 1 ou 2 Bilhete b1("nome Passageiro", 123, aux_nome, {1,2,3,4}); //{1,2,3,4} com initializer_list cout <<b1.getAsAstring(); //para atribuir objectos string aux_nome2("fly"); Bilhete b2("outro Passageiro",456, aux_nome2, {5,6,7,8}); cout << b2.getAsAstring(); //atribuir objectos b1 = b2 //não é possivel por existe objectos com membros objectos com referencia (&) ou const //assim b1=b2 dá erro, é a atribuição default //solução: operador atribuição e operator b1 = b2; cout << b1.getAsAstring(); //Mostrar o conteúdo no ecrã da forma (bilhete1 e bilhete2 são objetos da classe Bilhete): cout << "\nPassageiro 1 : " << b1 << "\nPassageiro 2" << b2; //Acrescentar bagagens ao bilhete (representadas pelos seus ID e sem repetir): b1 << 123 << 789 << 123; cout << "\nPassageiro 1 acrescentou: " << b1; //Remover todas as bagagens com id superior a um indicado): (b1 -= 40) -= 35; cout << "\nPassageiro 1 removeu: " << b1; cout << "\nfim do main" << endl; return 0; }
………..varios, exame1920_epocanormal (a25)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; //antes /* class Doutor { string nome; public: Doutor(const string& n) :nome(n) {} string getNome()const { return nome; } }; class Engenheiro { string nome; public: Engenheiro(const string& n) :nome(n) {} string getNome()const { return nome; } }; class Empresa { vector<Doutor> doutores; vector<Engenheiro> engenheiros; public: Empresa() { doutores.push_back(Doutor("D1")); doutores.push_back(Doutor("D2")); engenheiros.push_back(Engenheiro("E1")); engenheiros.push_back(Engenheiro("E2")); } void cumprimentar() { for (auto& d : doutores) { cout << "Bom dia Doutor " << d.getNome() << endl; } for (auto& e : engenheiros) { cout << "Bom dia Engenheiro " << e.getNome() << endl; } } void removeDoutor(string nome) { // remove o doutor com esse nome do seu vector } void removeEngenheiro(string nome) { // remove o engenheiro com esse nome do seu vector } }; */ //depois corrigido //class abstracta class Funcionario { string nome; public: Funcionario(string nome0) :nome(nome0){} virtual void cumprimentar() const = 0; string getNome()const { return nome; } //para funionar o destutor, construtor por copia e operador atribuição virtual Funcionario* duplica()const = 0; }; class Doutor: public Funcionario { public: Doutor(const string& nome0) : Funcionario(nome0) { } void cumprimentar() const override { cout << "Bom dia Doutor " << getNome() << endl; } Funcionario* duplica() const override { return new Doutor(*this); } }; class Engenheiro : public Funcionario { public: Engenheiro(const string& nome0) : Funcionario(nome0) { } void cumprimentar() const override { cout << "Bom dia Engenheiro " << getNome() << endl; } Funcionario* duplica() const override { return new Engenheiro(*this); } }; class Empresa { vector<Funcionario*> funcionarios; int pesquisa(string nome) const { for (unsigned int i = 0; i < funcionarios.size(); i++) { if (nome == funcionarios[i]->getNome()) { return i; } } return -1; } public: Empresa() { funcionarios.push_back(new Doutor("D1")); funcionarios.push_back(new Doutor("D2")); funcionarios.push_back(new Engenheiro("E1")); funcionarios.push_back(new Engenheiro("E2")); } void cumprimentar() { for (auto& d : funcionarios) { d->cumprimentar(); } } void removerNome(string nome0) { int pos = pesquisa(nome0); if(pos == -1) { return; } //existe posso exclucivsa, eliminar o objecto de mem dinamica delete funcionarios[pos]; //remover o ponteiro funcionarios.erase(funcionarios.begin() + pos); } //e existe propriedade exclusiva da empresa //fazer destrutor, construtor por copia e operador atribuição e duplica (esta em funcionarions) ~Empresa() { for(auto f: funcionarios) { delete f; } } //construtor por copia Empresa(const Empresa& ob) { *this = ob; //apenas, porque não existem ponteiros primitivos } //operador atribuição Empresa &operator=(const Empresa &ob) { if(this ==&ob) { return *this; } for(auto f: funcionarios) { delete f; } funcionarios.clear(); for(auto f: ob.funcionarios) { //depois de feitos os duplicas funcionarios.push_back(f->duplica()); } return *this; } }; int main() { Empresa empresa; empresa.cumprimentar(); Empresa empresa2 = empresa; empresa2.removerNome("E1"); empresa2.removerNome("D2"); empresa2.cumprimentar(); cout << "\nfim do main" << endl; return 1; }
herança e polimorfismo
………..ficha7, exercicio1 (a19)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class Imovel { float preco; float area; string codigo; static int ordem; //para formar o codigo //para ser usado por classes derivadas protected: //nao deixamos o codigo exterior alterar, mas apenas as classes derivadas void setCodigo(const string& c); public: Imovel(float p, float a); virtual ~Imovel(); float getPreco() const; void setPreco(const float preco); float getArea() const; string getCodigo() const; static int getOrdem(); virtual string getAsString()const; }; ostream& operator << (ostream& saida, const Imovel& i); //operador do cout int Imovel::ordem = 0; void Imovel::setCodigo(const string& c) { codigo = c; } Imovel::Imovel(float p, float a):preco(p), area(a) { //ou //ostringstream oss; //oss << (++ordem); //codigo = oss.str(); //ou codigo = to_string(++ordem); } Imovel::~Imovel() { } float Imovel::getPreco() const { return preco; } void Imovel::setPreco(const float preco) { this->preco = preco; } float Imovel::getArea() const { return area; } string Imovel::getCodigo() const { return codigo; } int Imovel::getOrdem() { return ordem; } string Imovel::getAsString() const { ostringstream oss; oss << " codigo: " << codigo << "\n preco: " << preco << "\n area " << area << endl; return oss.str(); } ostream& operator<<(ostream& saida, const Imovel& i) { saida << i.getAsString(); return saida; } //apartamento deriva de imovel, : public .. e recebe por herança class Apartamento : public Imovel { int numeroAssoalhadas; int andar; public: Apartamento(float area0, int andar0, int numeroAssoalhadas0); //getAstring é por herança //precisa de ser redifinida com o override string getAsString()const override; }; //o construtor d euma classe derivada já manda executar o construtor da calsse base: Imovel Apartamento::Apartamento(float area0, int andar0, int numeroAssoalhadas0):Imovel(10* area0, area0), andar(andar0),numeroAssoalhadas(numeroAssoalhadas0) { setCodigo("apartamento-" + getAsString()); } string Apartamento::getAsString() const { ostringstream oss; //preco area -Imovel::getAsString() oss << "apartamento \n" << Imovel::getAsString() << " andar: " << andar << "\n n. assoalhadas: " << numeroAssoalhadas << endl; return oss.str(); } class LojaComercial : public Imovel { //nao tem membros privados public: LojaComercial(float area); string getAsString()const override; }; LojaComercial::LojaComercial(float area):Imovel(15*area, area) { setCodigo("lojaComercial-" + getAsString()); } string LojaComercial::getAsString() const { ostringstream oss; //preco area -Imovel::getAsString() oss << "lojaComercial \n" << Imovel::getAsString() << " andar: R/C" << endl; return oss.str(); } class Imobiliaria { //"não deve utilizar uma estrutura de dados para cada tipo diferente de bem imobiliário" //não usar multiplos vectores, um para cada tipo, mas um para todos //assim: vector<Imovel*> imoveis; //coleção de vários tipos de imoveis //um ponteiro de uma classe derivada pode apontar para a classe base, upcasting //relação entre imobiliaria e imoveis : agregação string nome; int pesquisa(string codigo)const; public: Imobiliaria(string nome); void acrescentaImovel(Imovel* imovel); //recebemos um imovel qualquer //ponteiro para a base bool setPreco(string codigo, float preco); float getPreco(string codigo)const; bool remover(string codigo); string getAsString()const; string getAsString(string codgio0)const; }; ostream& operator << (ostream& saida, const Imobiliaria& i); //operador do cout int Imobiliaria::pesquisa(string codigo) const { for(unsigned int i = 0; 0 < imoveis.size() ; i++) { if(codigo == imoveis[i]->getCodigo()) { return i; } } return -1; } Imobiliaria::Imobiliaria(string nome):nome(nome) { } void Imobiliaria::acrescentaImovel(Imovel* imovel) { if(imovel != nullptr) { //pode surigr uma loja ou apartamento imoveis.push_back(imovel); } } bool Imobiliaria::setPreco(string codigo, float preco) { if (preco < 0){ return false; } int qual = pesquisa(codigo); if (qual == -1) { return false; } imoveis[qual]->setPreco(preco); return true; } float Imobiliaria::getPreco(string codigo) const { int qual = pesquisa(codigo); if(qual == -1) { return 0; } return imoveis[qual]->getPreco(); } bool Imobiliaria::remover(string codigo) { int qual = pesquisa(codigo); if(qual == -1) { return false; } //relação de agregação faz-se apenas o erase imoveis.erase(imoveis.begin() + qual); return true; } string Imobiliaria::getAsString() const { ostringstream oss; oss << "Imobiliaria " << nome << endl; for(unsigned int i = 0; i < imoveis.size(); i++) { oss << imoveis[i]->getAsString() << endl; } return oss.str(); } string Imobiliaria::getAsString(string codgio0) const { int qual = pesquisa(codgio0); if(qual == -1) { return "codigo nao existe"; } return imoveis[qual]->getAsString(); } ostream& operator<<(ostream& saida, const Imobiliaria& i) { saida << i.getAsString(); return saida; } //extra Interaccao class Interaccao { Imobiliaria* imobiliaria; //classe que envolve toda a lógica public: Interaccao(Imobiliaria* imobiliaria0); int escolheOpcao(vector<string> opcoes); int lerInt(string msg); float lerFloat(string msg); void opcaoAcrescentarApartamento(); void opcaoAcrescentarLoja(); void opcaoPesquisarImovelPorCodigo(); void opcaoPesquisarPrecoDeImovelPorCodigo(); void opcaoActualizarPrecoDeImovelPorCodigo(); void opcaoRemoverImovelPorCodigo(); void corre(); }; Interaccao::Interaccao(Imobiliaria* imobiliaria0) { imobiliaria = imobiliaria0; } int Interaccao::escolheOpcao(vector<string> opcoes) { for (unsigned int i = 0; i < opcoes.size(); i++) cout << endl << i << " - " << opcoes[i]; int opcao = -1; do { opcao = lerInt("\n\nopcao > "); } while (opcao < 0 || opcao > opcoes.size()); return opcao; } int Interaccao::lerInt(string msg) { int valor; bool leu = false; do { cout << msg; string s; cin >> s; istringstream iss(s); if (iss >> valor) // se correu bem a leitura leu = true; } while (!leu); return valor; } float Interaccao::lerFloat(string msg) { float valor; bool leu = false; do { cout << msg; string s; cin >> s; istringstream iss(s); if (iss >> valor) // se correu bem a leitura leu = true; } while (!leu); return valor; } void Interaccao::opcaoAcrescentarApartamento() { float area; int andar; int nAss; cout << "\nAcrescentar apartamento"; area = lerFloat(" area: "); andar = lerInt(" andar: "); nAss = lerInt(" n. de assoalhadas: "); imobiliaria->acrescentaImovel(new Apartamento(area, andar, nAss)); } void Interaccao::opcaoAcrescentarLoja() { float area; cout << "\nAcrescentar loja: "; area = lerFloat(" area: "); imobiliaria->acrescentaImovel(new LojaComercial(area)); } void Interaccao::opcaoPesquisarImovelPorCodigo() { string codigo; cout << "\nPesquisar imovel por codigo\n codigo: "; cin >> codigo; cout << imobiliaria->getAsString(codigo) << endl; } void Interaccao::opcaoPesquisarPrecoDeImovelPorCodigo() { string codigo; cout << "\nPesquisar preco de imovel por codigo\n codigo: "; cin >> codigo; cout << imobiliaria->getPreco(codigo) << endl; } void Interaccao::opcaoActualizarPrecoDeImovelPorCodigo() { string codigo; float preco; cout << "\nActualizar preco de imovel por codigo\n codigo: "; cin >> codigo; cout << imobiliaria->getAsString(codigo) << endl; preco = lerFloat("\n preco: "); imobiliaria->setPreco(codigo, preco); } void Interaccao::opcaoRemoverImovelPorCodigo() { string codigo; cout << "\nRemover imovel por codigo\n codigo: "; cin >> codigo; cout << imobiliaria->getAsString(codigo) << endl; cout << "\n Remover "; if (imobiliaria->remover(codigo)) cout << " removeu "; else cout << " nao removeu "; } void Interaccao::corre() { vector<string> opcoes; opcoes.push_back("Sair"); opcoes.push_back("Listar"); opcoes.push_back("Acrescentar apartamento"); opcoes.push_back("Acrescentar loja"); opcoes.push_back("Pesquisar imovel por codigo"); opcoes.push_back("Pesquisar preco de imovel dado o codigo"); opcoes.push_back("Actualizar o preco de um imovel dado o codigo"); opcoes.push_back("Remover imovel dado o codigo"); int opcao = 0; do { opcao = escolheOpcao(opcoes); switch (opcao) { case 0: cout << "\nSair\n"; break; case 1: cout << *imobiliaria << endl; break; case 2: opcaoAcrescentarApartamento(); break; case 3: opcaoAcrescentarLoja(); break; case 4: opcaoPesquisarImovelPorCodigo(); break; case 5: opcaoPesquisarPrecoDeImovelPorCodigo(); break; case 6: opcaoActualizarPrecoDeImovelPorCodigo(); break; case 7: opcaoRemoverImovelPorCodigo(); break; } } while (opcao != 0); } int main() { Imobiliaria* imobiliaria = new Imobiliaria("Imobiliaria"); Interaccao interaccao(imobiliaria); interaccao.corre(); delete imobiliaria; cout << "\nfim do main.." << endl; return 1; }
………..ficha7, exercicio3 (a19,a20)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; #include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; /* +---------------------------------------- + -------------------------- - + | operador | membro / não membro | +---------------------------------------- + -------------------------- - + | todos os operadores unários p.e: ++a, a++ | membro | | = () -> ->* | têm sempre que ser membro | | += -= /= *= &= |= %= >>= <<= | membro | | todos os restantes operadores binários, ==,<<... | globais | +--- */ //relação de agregaçao, memoria dinâmica class Livro { string titulo; string autor; long isbn; public: Livro(const string& titulo0, const string& autor0, const long isbn0); virtual ~Livro(); //classe de hierarquia, boa pratica string getTitulo() const; void setTitulo(const string& titulo); string getAutor() const; void setAutor(const string& autor); long getIsbn() const; void setIsbn(const long isbn); virtual string getAsString()const; //para adaptar aos objectos das classes derivadas virtual Livro* duplica() const; }; //comparação de dois livros bool operator==(const Livro& ob1, const Livro& ob2); // cout objecto-de-livro ostream& operator<<(ostream& saida, const Livro& lv); Livro::Livro(const string& titulo0, const string& autor0, const long isbn0):titulo(titulo0), autor(autor0), isbn(isbn0) { } Livro::~Livro() { } string Livro::getTitulo() const { return titulo; } void Livro::setTitulo(const string& titulo) { this->titulo = titulo; } string Livro::getAutor() const { return autor; } void Livro::setAutor(const string& autor) { this->autor = autor; } long Livro::getIsbn() const { return isbn; } void Livro::setIsbn(const long isbn) { this->isbn = isbn; } string Livro::getAsString() const { ostringstream oss; cout << "\ntitulo: " << titulo << " autor: " << autor << " isbn " << isbn << endl; return oss.str(); } //criar um clone Livro* Livro::duplica() const { //ou //return new Livro(*this); //ou Livro* lv = new Livro(*this); return lv; } bool operator==(const Livro& ob1, const Livro& ob2) { return ob1.getIsbn() == ob2.getIsbn(); } ostream& operator<<(ostream& saida, const Livro& lv) { saida << lv.getAsString(); return saida; } class LivroFiccao : public Livro { string nomePlaneta; int anoAccao; bool realidadeAccao; //0 realista, 1 fatansiosa public: LivroFiccao(const string& titulo0, const string& autor0, const long isbn0, const string& nome_planeta, const int ano_accao, const bool realidade_accao); string getNomePlaneta() const; int getAnoAccao() const; bool getRealidadeAccao() const; string getAsString() const override; Livro * duplica() const override; }; LivroFiccao::LivroFiccao(const string& titulo0, const string& autor0, const long isbn0, const string& nome_planeta, const int ano_accao, const bool realidade_accao) : Livro(titulo0, autor0, isbn0), nomePlaneta(nome_planeta), anoAccao(ano_accao),realidadeAccao(realidade_accao){ } string LivroFiccao::getNomePlaneta() const { return nomePlaneta; } int LivroFiccao::getAnoAccao() const { return anoAccao; } bool LivroFiccao::getRealidadeAccao() const { return realidadeAccao; } string LivroFiccao::getAsString() const { ostringstream oss; oss << endl << Livro::getAsString() << " planeta " << nomePlaneta << " ano " << anoAccao << endl; if(realidadeAccao) { oss << " realista \n"; }else { oss << " fantasiosa \n"; } return oss.str(); } Livro* LivroFiccao::duplica() const { return new LivroFiccao(*this); } class LivroPolicial: public Livro { string nomeDetective; int numeroTiros; public: LivroPolicial(const string& titulo0, const string& autor0, const long isbn0, const string& nomeDetective0, int numeroTiros = 0); string getNomeDetective() const; int getNumeroTiros() const; string getAsString()const override; Livro* duplica()const override; }; LivroPolicial::LivroPolicial(const string& titulo0, const string& autor0, const long isbn0, const string& nomeDetective0, int numeroTiros0):Livro(titulo0, autor0, isbn0), nomeDetective(nomeDetective0),numeroTiros(numeroTiros0) { } string LivroPolicial::getNomeDetective() const { return nomeDetective; } int LivroPolicial::getNumeroTiros() const { return numeroTiros; } string LivroPolicial::getAsString() const { ostringstream oss; oss << endl << Livro::getAsString() << " detective: " << nomeDetective << endl; if(numeroTiros > 10) { oss << " nao aconcelhado a criancas \n"; }else { oss << " n. de tiros " << numeroTiros << endl; } return oss.str(); } //nas classes concretas tem que haver um duplica Livro* LivroPolicial::duplica() const { //ou //return new LivroPolicial(*this); //ou LivroPolicial* lvp = new LivroPolicial(*this); return lvp; } //bibliteca tem relação de composição com o livro //posse exclusiva, implementar: destrutor, construtor por copia, operador atribuição class Biblioteca { string morada; //qualquer tipo de livros: ponteiros para a classe base, que depois tem acesso às derivadas vector<Livro *> livros; //vector polimorfico, <Livro *> int pesquisaLivro(long isbn0) const; //indice, int public: Biblioteca(const string& morada); bool acrescentarLivro(Livro * livro); bool removerLivro(long isbn0); //por haver relação de composição, os livros não sobrevivem à destruição da biblioteca //e destrutor virtual ~Biblioteca(); //e construtor por copia Biblioteca(const Biblioteca& ob); //e operador atribuição Biblioteca& operator=(const Biblioteca & ob); string getAsString()const; }; ostream& operator<<(ostream& saida, const Biblioteca& bibl); int Biblioteca::pesquisaLivro(long isbn0) const { for(unsigned int i=0; i<livros.size(); i++) { if(isbn0 == livros[i]->getIsbn()) { return i; } } return -1; } Biblioteca::Biblioteca(const string& morada) { } bool Biblioteca::acrescentarLivro(Livro* livro) { if(livro == nullptr || pesquisaLivro(livro->getIsbn())!=-1) { return false; } Livro* lv = livro->duplica(); //é duplicado com o livro->duplica();, não aceito o que me dão livros.push_back(lv); return true; } bool Biblioteca::removerLivro(long isbn0) { int qual = pesquisaLivro(isbn0); if(qual == -1) { return false; } //liberta o obejcto apontado por livro[i] delete livros[qual]; //só por ser relação de composição //retirar o vector o pionteito que aponta para memoria já libertada livros.erase(livros.begin() + qual); return true; } Biblioteca::~Biblioteca() { for(Livro * l: livros) { delete l; } } Biblioteca::Biblioteca(const Biblioteca& ob) { //nao ha ponteiros com valores lixo para limpar //funcionou o construtor por omissão do vector que criou //um vector vazio (sem ponteiros) //e do membro do tipo string //se houvesse ponteiros com valores lixo punha-se a nullptr //porque precisamos de preparar o objecto que esta aser criado //para invocar o operador de atribuição que limpa a memoria velha //do objecto que o invoca, o primeiro membro da atribuição *this = ob; //chama o operador atribuicao e copia os dados //de acordo com o que tem implementado: duplicando //a memoria dinmica que o destrutor liberta } Biblioteca& Biblioteca::operator=(const Biblioteca& ob) { //prevenção de auto atribuição if(this == & ob) { return *this; } //limpesa da memoria velha do primeiro membro de atribuicao for(Livro * lv: livros) { delete lv; } //esvaziar o vector que agora só aponta para memoria ja nao reservada livros.clear(); //duplicar os objectos dinamicos, copiar a informacao de ob for(Livro * lv: livros) { Livro* p = lv->duplica(); livros.push_back(lv); } //e nao esquecer os restantes membros morada = ob.morada; return *this; } string Biblioteca::getAsString() const { ostringstream oss; oss << "\nmorada: " << morada << endl; for(unsigned int i=0; i<livros.size(); i++) { oss << livros[i]->getAsString(); } return oss.str(); } // cout objecto-de-livro ostream& operator<<(ostream& saida, const Biblioteca & bibl) { saida << bibl.getAsString(); return saida; } int main() { Biblioteca* b = new Biblioteca("aldeia de coimbra"); Livro* p = new LivroPolicial("coimbra", "coimbra", 12345,"coimbra", 1); b->acrescentarLivro(p); cout << b->getAsString(); cout << "\nfim do main" << endl; return 1; }
………..ficha?, exercicio? (a21), aquario polimorfico
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class Pargo; class Pescada; class Aquario; class Peixe; //peixe é uma classe abstracta, não se podem criar objectos deste tipo class Peixe { string nomeEspecie; string cor; int peso; int numSerie; static int sequencia; //constantes iguais para todos: static const int INICIAL_PESO = 10; bool vivo = true; //importante, subs estadio protected: void aumentaPeso(int quant0); public: Peixe(string nomeEspecie0, string cor0 = "cinzento"); //nao existe aqui construtor default //assim a classe derivada precisa de um construtor que alimente a classe base Peixe(const Peixe& orig); //existe porque precisamos que os peixes tenham um num de seq diferente //tudo é copiado mas o num de série é diferente. ///se fosse o natrual copiaava tudo int getNumSerie() const; int getPeso() const; bool isVivo()const; virtual string getAsString()const; //funcao abstracta nao se podem criar objectos desta classe //pode haver "Peixe *" e "Peixe &" virtual void alimentar(int quant, Aquario* aquario) = 0; virtual Peixe* duplica()const = 0; //serve para criar diferentes tipos de peixes: pargo e pescada static Peixe* fabrica(string tipo0, string cor0="cinzento"); virtual ~Peixe(); bool setVivo(bool v); }; ostream& operator<<(ostream& saida, const Peixe& p); void Peixe::aumentaPeso(int quant0) { peso +=quant0; } Peixe::Peixe(string nomeEspecie0, string cor0) :nomeEspecie(nomeEspecie0), cor(cor0), peso(INICIAL_PESO), numSerie(sequencia++) { } Peixe::Peixe(const Peixe& orig):nomeEspecie(orig.nomeEspecie), cor(orig.cor), peso(orig.peso), numSerie(sequencia++) { //o peixe construido por copia tem outro numero de série } int Peixe::getPeso() const { return peso; } int Peixe::getNumSerie() const { return numSerie; } bool Peixe::isVivo() const { return vivo; } Peixe* Peixe::fabrica(string tipo0, string cor0) { if(tipo0 == "pargo") { return new Pargo(cor0); }else if(tipo0 == "pescada") { return new Pescada; } return nullptr; } bool Peixe::setVivo(bool v) { vivo = v; } ostream& operator<<(ostream& saida, const Peixe& p) { saida << p.getAsString(); return saida; } string Peixe::getAsString() const { ostringstream oss; oss << "\nPeixe: " << nomeEspecie << "\ncor: " << cor << "\npeso: " << peso << "\nnumSerie: " << numSerie << endl; return oss.str(); } int Peixe::sequencia = 499; //num. para cada peixe //NOVO: surgem duas espécies de peixes class Pargo: public Peixe { static const int LIMITE_PESO = 50; public: Pargo(string cor0 = "cinzento"); void alimentar(int quant0, Aquario* aquario) override; Peixe* duplica()const override; }; Pargo::Pargo(string cor0):Peixe("pargo",cor0) { //importante pargo precisa de passar o que o construtor da base precisa, o Peixe //porque a base não é default } void Pargo::alimentar(int quant0, Aquario* aquario) { if(quant0<=0 || aquario==nullptr) { return; } aumentaPeso(quant0); if(getPeso()>LIMITE_PESO) { //aquario->eliminaPeixe(getNumSerie()); //perigo setVivo(false); } } Peixe* Pargo::duplica() const { //duplicação polimórfica return new Pargo(*this); } class Pescada : public Peixe { static const int LIMITE_SOLIDAO = 1; static const int LIMITE_MULTIDAO = 10; public: Pescada(); void alimentar(int quant0, Aquario* aquario0) override; Peixe* duplica()const override; }; Pescada::Pescada():Peixe("pescada") { } void Pescada::alimentar(int quant0, Aquario* aquario0) { if(quant0<=0 || aquario0 == nullptr) { return; } int num = aquario0->getQuantosPeixes(); //uma estragégia para aceder ao aquario //ou colocar na classe peixe um ponteiro para aquario //assim as derivadas teriam sempre acesso if(num> LIMITE_SOLIDAO && num < LIMITE_MULTIDAO) { aumentaPeso(quant0); }else { aumentaPeso(quant0 / 2); } } Peixe* Pescada::duplica() const { return new Pescada(*this); } //aquario é uma classe abstracta class Aquario { vector<Peixe*> peixes; //vector de ponteiros int pesquisaPeixe(int numSerie0)const; void eliminarMortos(); public: Aquario() = default; //qd criado é um vector vazio //quando um peixe e posto no aquario, este assume a //sua posse e controlo total Aquario(const Aquario& orig); //construtor por cópia Aquario& operator=(const Aquario& orig); //operador atribuição virtual ~Aquario(); bool addPeixe(string tipo0, string cor0 = "cinzento"); const Peixe* getPeixe(int numSerie) const; void alimentar(int quantidade0); void removePeixe(int numSerie); bool eliminaPeixe(int numSerie0); string getAsString()const; unsigned int getQuantosPeixes()const; }; void Aquario::alimentar(int quantidade0) { //for (Peixe* p : peixes) //{ // p->alimentar(quantidade0, this); //mt cuidado com isto, usa o vector<Peixe*> novos; //} //v2 for(int i = 0; i < peixes.size(); i++) { peixes[i]->alimentar(quantidade0, this); //percorrer o vector } eliminarMortos(); //vou verificar o boolenao vivo dos peixes } string Aquario::getAsString() const { ostringstream oss; oss << "\nAquario; " << endl; //neste caso vai ser contatenada a informação //para o objecto que estiver a ser apontado //não interessa qual for (Peixe* p : peixes) { oss << p->getAsString(); } return oss.str(); } unsigned Aquario::getQuantosPeixes() const { return peixes.size(); } bool Aquario::addPeixe(string tipo0, string cor0) { Peixe* peixe = Peixe::fabrica(tipo0, cor0); //fabrica é static por isso :: if(peixe==nullptr) { return false; } peixes.push_back(peixe); return true; } const Peixe* Aquario::getPeixe(int numSerie) const { int qual = pesquisaPeixe(numSerie); if(qual == -1) { return nullptr; } return peixes[qual]; }; int Aquario::pesquisaPeixe(int numSerie0)const { for (int i = 0; i < peixes.size(); i++) { if (peixes[i]->getNumSerie() == numSerie0) { return i; } } return -1; } void Aquario::eliminarMortos() { //formula //ou //for(vector<Peixe*>::iterator it = peixes.begin() ; it != peixes.end(); ) //ou for (auto it = peixes.begin(); it != peixes.end();) { if (!(*it)->isVivo()) { delete (*it); it = peixes.erase(it); } else { ++it; } } } Aquario::~Aquario() { for (Peixe* p : peixes) { delete p; } cout << "\n~Aquario()" << endl; } Aquario::Aquario(const Aquario& orig) { //funcionou o constru por defeito do vector //o vector está vazio //não há ponteiros com lixo *this = orig; } Aquario& Aquario::operator=(const Aquario& orig) { //prevenção da auto atribuição if (this == &orig) { return *this; } //libtertar mem dina velha for (Peixe* p : peixes) { delete p; } //esvaziar o vector peixes.clear(); //copiar a informacao de orig, duplicando os objectos dinamicos for (Peixe * peixe : orig.peixes) { Peixe * p = peixe->duplica(); peixes.push_back(p); } return *this; } void Aquario::removePeixe(int numSerie) { int qual = pesquisaPeixe(numSerie); if (qual == -1) { return; } delete peixes[qual]; peixes.erase(peixes.begin() + qual); } bool Aquario::eliminaPeixe(int numSerie0) { int qual = pesquisaPeixe(numSerie0); if(qual == -1) { return false; } //remove o peixe do aquario, é destruido delete peixes[qual]; //depois de feito o dele (libertada a memoria) //precisamos de retirar do vector //o ponteio para memoria ja nao reservada peixes.erase(peixes.begin() + qual); return true; } int main() { cout << "\nfim do main" << endl; return 1; }
#include <string> #include <iostream> #include <sstream> #include <vector> using namespace std; class Caderno { private: //membros variáveis string marca; string cor; int numFolhas; string tamanho; public: Caderno(const string& marca, const string& cor, const int num_folhas, const string& tamanho) //const não consigo alterar o argumento que vem da main, assim fica mais eficiente/seguro, //e tambem assim posso fazer uso de uma const, usando por exemplo "marca" //o int pode ter const ou não e referencia, e porque é uma coisa pequena : marca(marca), cor(cor), numFolhas(num_folhas), tamanho(tamanho) { } //membros funções //funções get //os consts sigificam que não podem alterar nada, é uma função de consulta string getMarca() const { return marca; } string getCor() const { return cor; } int getNumfolhas() const { return numFolhas; } string getTamanho() const { return tamanho; } //funções set void setMarca(const string & marca) { this->marca = marca; } void setCor(const string & cor) { this->cor = cor; } void setNumfolhas(int num_folhas) { if(num_folhas>0){ numFolhas = num_folhas; }else { numFolhas = 0; } } void setTamanho(const string& tamanho) { this->tamanho = tamanho; } string getAsString()const; }; string Caderno::getAsString()const { ostringstream oss; oss << "\nmarca: " << marca << "\ncor: " << cor << "\nnum folhas: " << numFolhas << "n\ntamanho: " << tamanho; return oss.str(); } int main() { const Caderno Cad1("bic","branca",2,"a4"); //const não posso alterar cout << Cad1.getAsString(); cout << "\nfim do main" << endl; return 0; }
………..ficha?, exercicio? (a21, a22), aquario polimorfico v2
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class Aquario; class Pargo; class Pescada; //peixe é uma classe abstracta, não se podem criar objectos deste tipo class Peixe { string nomeEspecie; string cor; int peso; int numSerie; static int sequencia; //constantes iguais para todos: static const int INICIAL_PESO = 10; bool vivo = true; //importante, subs estadio //v2 Aquario* aquario = nullptr; protected: void aumentaPeso(int quant0); Aquario* getAquario(); //v2 public: Peixe(Aquario* aq0, string nomeEspecie0, string cor0 = "cinzento"); //v2 //nao existe aqui construtor default //assim a classe derivada precisa de um construtor que alimente a classe base Peixe(const Peixe& orig); //existe porque precisamos que os peixes tenham um num de seq diferente //tudo é copiado mas o num de série é diferente. ///se fosse o natrual copiaava tudo int getNumSerie() const; int getPeso() const; bool isVivo()const; virtual string getAsString()const; //funcao abstracta nao se podem criar objectos desta classe //pode haver "Peixe *" e "Peixe &" //virtual void alimentar(int quant, Aquario* aquario) = 0; virtual void alimentar(int quant) = 0; //v2 virtual Peixe* duplica()const = 0; //serve para criar diferentes tipos de peixes: pargo e pescada static Peixe* fabrica(string tipo0, Aquario * aq0, string cor0="cinzento"); virtual ~Peixe(); bool setVivo(bool v); //v3 void setAquario(Aquario* aqua0); }; ostream& operator<<(ostream& saida, const Peixe& p); void Peixe::aumentaPeso(int quant0) { peso +=quant0; } Aquario* Peixe::getAquario() { return aquario; } Peixe::Peixe(Aquario * aq0, string nomeEspecie0, string cor0) :aquario(aq0),nomeEspecie(nomeEspecie0), cor(cor0), peso(INICIAL_PESO), numSerie(sequencia++) { } Peixe::Peixe(const Peixe& orig):nomeEspecie(orig.nomeEspecie), cor(orig.cor), peso(orig.peso), numSerie(sequencia++) { //o peixe construido por copia tem outro numero de série } int Peixe::getPeso() const { return peso; } int Peixe::getNumSerie() const { return numSerie; } bool Peixe::isVivo() const { return vivo; } Peixe* Peixe::fabrica(string tipo0, Aquario * aqu0, string cor0) { if(tipo0 == "pargo") { return new Pargo(aqu0, cor0); }else if(tipo0 == "pescada") { return new Pescada(aqu0); } return nullptr; } bool Peixe::setVivo(bool v) { vivo = v; } void Peixe::setAquario(Aquario* aqua0) { this->aquario = aqua0; } ostream& operator<<(ostream& saida, const Peixe& p) { saida << p.getAsString(); return saida; } string Peixe::getAsString() const { ostringstream oss; oss << "\nPeixe: " << nomeEspecie << "\ncor: " << cor << "\npeso: " << peso << "\nnumSerie: " << numSerie << endl; return oss.str(); } int Peixe::sequencia = 499; //num. para cada peixe //NOVO: surgem duas espécies de peixes class Pargo: public Peixe { static const int LIMITE_PESO = 50; public: Pargo(Aquario * aqua0, string cor0 = "cinzento"); void alimentar(int quant0) override; Peixe* duplica()const override; }; Pargo::Pargo(Aquario* aqua0, string cor0):Peixe(aqua0, "pargo",cor0) { //importante pargo precisa de passar o que o construtor da base precisa, o Peixe //porque a base não é default } void Pargo::alimentar(int quant0) { if(quant0<=0 || getAquario()==nullptr) { return; } aumentaPeso(quant0); if(getPeso()>LIMITE_PESO) { //aquario->eliminaPeixe(getNumSerie()); //perigo setVivo(false); } } Peixe* Pargo::duplica() const { //duplicação polimórfica return new Pargo(*this); } class Pescada : public Peixe { static const int LIMITE_SOLIDAO = 1; static const int LIMITE_MULTIDAO = 10; public: Pescada(Aquario * aqu0); void alimentar(int quant0) override; Peixe* duplica()const override; }; Pescada::Pescada(Aquario* aqu0):Peixe(aqu0, "pescada") { } void Pescada::alimentar(int quant0) { if(quant0<=0 || getAquario() == nullptr) //v2 { return; } int num = getAquario()->getQuantosPeixes(); //uma estragégia para aceder ao aquario, v2 //ou colocar na classe peixe um ponteiro para aquario //assim as derivadas teriam sempre acesso if(num> LIMITE_SOLIDAO && num < LIMITE_MULTIDAO) { aumentaPeso(quant0); }else { aumentaPeso(quant0 / 2); } } Peixe* Pescada::duplica() const { return new Pescada(*this); } //aquario é uma classe abstracta class Aquario { vector<Peixe*> peixes; //vector de ponteiros int pesquisaPeixe(int numSerie0)const; void eliminarMortos(); public: Aquario() = default; //qd criado é um vector vazio //quando um peixe e posto no aquario, este assume a //sua posse e controlo total Aquario(const Aquario& orig); //construtor por cópia Aquario& operator=(const Aquario& orig); //operador atribuição virtual ~Aquario(); bool addPeixe(string tipo0, string cor0 = "cinzento"); const Peixe* getPeixe(int numSerie) const; void alimentar(int quantidade0); void removePeixe(int numSerie); bool eliminaPeixe(int numSerie0); string getAsString()const; unsigned int getQuantosPeixes()const; }; void Aquario::alimentar(int quantidade0) { //for (Peixe* p : peixes) //{ // p->alimentar(quantidade0, this); //mt cuidado com isto, usa o vector<Peixe*> novos; //} //v2 for(int i = 0; i < peixes.size(); i++) { peixes[i]->alimentar(quantidade0); //percorrer o vector, v2 } eliminarMortos(); //vou verificar o boolenao vivo dos peixes } string Aquario::getAsString() const { ostringstream oss; oss << "\nAquario; " << endl; //neste caso vai ser contatenada a informação //para o objecto que estiver a ser apontado //não interessa qual for (Peixe* p : peixes) { oss << p->getAsString(); } return oss.str(); } unsigned Aquario::getQuantosPeixes() const { return peixes.size(); } bool Aquario::addPeixe(string tipo0, string cor0) { Peixe* peixe = Peixe::fabrica(tipo0, this, cor0); //fabrica é static por isso ::, v2 if(peixe==nullptr) { return false; } peixes.push_back(peixe); return true; } const Peixe* Aquario::getPeixe(int numSerie) const { int qual = pesquisaPeixe(numSerie); if(qual == -1) { return nullptr; } return peixes[qual]; }; int Aquario::pesquisaPeixe(int numSerie0)const { for (int i = 0; i < peixes.size(); i++) { if (peixes[i]->getNumSerie() == numSerie0) { return i; } } return -1; } void Aquario::eliminarMortos() { //formula //ou //for(vector<Peixe*>::iterator it = peixes.begin() ; it != peixes.end(); ) //ou for (auto it = peixes.begin(); it != peixes.end();) { if (!(*it)->isVivo()) { delete (*it); it = peixes.erase(it); } else { ++it; } } } Aquario::~Aquario() { for (Peixe* p : peixes) { delete p; } cout << "\n~Aquario()" << endl; } Aquario::Aquario(const Aquario& orig) { //funcionou o constru por defeito do vector //o vector está vazio //não há ponteiros com lixo *this = orig; } Aquario& Aquario::operator=(const Aquario& orig) { //prevenção da auto atribuição if (this == &orig) { return *this; } //libtertar mem dina velha for (Peixe* p : peixes) { delete p; } //esvaziar o vector peixes.clear(); //copiar a informacao de orig, duplicando os objectos dinamicos for (Peixe * peixe : orig.peixes) { Peixe * p = peixe->duplica(); p->setAquario(this); //para aponter para o aquario novo, v3 peixes.push_back(p); } return *this; } void Aquario::removePeixe(int numSerie) { int qual = pesquisaPeixe(numSerie); if (qual == -1) { return; } delete peixes[qual]; peixes.erase(peixes.begin() + qual); } bool Aquario::eliminaPeixe(int numSerie0) { int qual = pesquisaPeixe(numSerie0); if(qual == -1) { return false; } //remove o peixe do aquario, é destruido delete peixes[qual]; //depois de feito o dele (libertada a memoria) //precisamos de retirar do vector //o ponteio para memoria ja nao reservada peixes.erase(peixes.begin() + qual); return true; } int main() { Aquario* aq1 = new Aquario; aq1->addPeixe("pescada"); aq1->addPeixe("pargo", "laranja"); cout << aq1->getAsString() << endl; Aquario* aq2 = new Aquario; aq2->addPeixe("pargo", "azul"); cout << aq2->getAsString() << endl; *aq2 = *aq1; cout << aq2->getAsString() << endl; aq2->alimentar(10); cout << aq2->getAsString() << endl; cout << "\nfim do main" << endl; return 1; }
………..ficha7, exercicio4 (a22, a23) exercicio global
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> class Ginasio; using namespace std; class Tarifario { //sem usar contentores da STL //(vector, lista, maps.. ) //e implica: memoria dinamica, destrutor, operador atribuição, construtor por cópia //deve ser usado um array dinamico de inteiros unsigned int* treinos; //ponteiro para o array dinamico unsigned int quantos; //quantidade de elementos, pois não podemos usar o size(), serve para controlar qts //vai ser implementada pelas derivadas //esta função apenas se destina a calcular o pagamento //a função calculaPagamentoEApagaTreinos invoca esta e apaga os treinos virtual unsigned int calculaPagamento()const = 0; //abstracta public: void acrescentaTreino(unsigned int at); unsigned int calculaPagamentoEApagaTreinos(); //é preciso o construtor para inicializar o ponteiro e a variavel quantos Tarifario(); //este é um construtor por omissão //vai ser preciso ter o destrutor para devolver a memoria ocupada pela matriz dinamica virtual ~Tarifario(); //existem derivas esta é virtual //justificação de: //a matriz dinamica de inteiros(treinos) pertence exclusivamente a cada objeto //de tarifario mas nao e automaticamente copiada quando se copiam ou atribuem estes //objectos (apenas o ponteiro e copiado lvando a partilha de matriz entre objectos //e mais tarde a deletes repetidos da mesma matriz, entre outras incoerencias) // => é preciso fazer o operador de atribuição e o construtor por cópia Tarifario(const Tarifario& orig); Tarifario& operator=(const Tarifario& orig); unsigned int getNumTreinos()const; unsigned int getTreino(unsigned int i)const; virtual string getAsString() const; //tem que ser abstracta tb. nao se pode construir //um tarifario ainda porque é classe abstracta virtual Tarifario* duplica()const = 0; //duplicação polimorfica, por causa do operador atribuição do cliente }; void Tarifario::acrescentaTreino(unsigned at) { //ao inves do push back unsigned int* aux = new unsigned int[quantos + 1]; for(unsigned int i=0; i< quantos; i++) { aux[i] = treinos[i]; } aux[quantos] = at; quantos++; delete[] treinos; treinos = aux; } unsigned Tarifario::calculaPagamentoEApagaTreinos() { //vai chamar a função correspondente a classe do objeto que invoca unsigned int conta = calculaPagamento(); //definido nas classes concretas calculaPagamento(); delete[] treinos; treinos = nullptr; quantos = 0; return conta; } Tarifario::Tarifario() { treinos = nullptr; //inicialmente nao existem treinos quantos = 0; } Tarifario::~Tarifario() { delete[] treinos; //libertar a memoria dinamica } Tarifario::Tarifario(const Tarifario& orig) { //pre inicialização para que o operador de atribuição //consiga trabalhar bem ( precisa de ter o objecto num estado //coerente dai a inicialização previa //para acontecer o delete apenas a um valor de um ponteiro //nulo ou com um valor dado por new treinos = nullptr; //treinos é um vector ponteiro que nasce com lixo, por isso é impts isto! quantos = 0; //usar o operador de atribuição *this = orig; } Tarifario& Tarifario::operator=(const Tarifario& orig) //a= b { //testa auto-aribuição para evitar trabalho desnecssário e possivel incorencia //e libertação dos treinos da orgem da copia (que coincide com o destino) //na memoria diamica se nao o mesmo -> ja sao iguaos = concluido if(this == &orig) { return *this; } //devolve os recursos ocupados pelo objecto alvo da atribuição delete[] treinos; //treinos é um vector ponteiro que nasce com lixo treinos = nullptr; quantos = 0; //se o outro objecto tambem tem 0 treinos ja estamos iguais. concluido. if(orig.treinos == nullptr) { return * this; } //senão: //criar recursos iguais (copiar) aos do outro objecto //alocar matriz dnamica para tantos treinos como os do outro objecto //copiar dos dados: o conteudo da matriz e restantes membros treinos = new unsigned int[orig.quantos]; quantos = orig.quantos; for(unsigned int i = 0 ; i < quantos; i++) { treinos[i] = orig.treinos[i]; //copiar todos } return *this; } unsigned Tarifario::getNumTreinos() const { return quantos; } unsigned Tarifario::getTreino(unsigned i) const { if(i<= 0|| (i>=quantos)) { return 0; } return treinos[i]; } string Tarifario::getAsString() const { ostringstream oss; oss << " tarifario: " << endl; return oss.str(); } //relação com tarifario é de composição, sento Apressado uma sub-classe //apressado é uma classe derivada concreta e tem que existir: //tem que se concretirar tdoas as funções abstractas //tem que haver um construtor que alimenta a classe base, se tiver argumentos o construtor da base //mas neste caso é um construtor por omissão class Apressado :public Tarifario { unsigned int calculaPagamento() const override; //é abstracta na classe tarifario (virtual) public: Tarifario * duplica() const override; //é abstracta na classe tarifario (virtual) string getAsString() const override; }; unsigned Apressado::calculaPagamento() const { unsigned int custo = 0; unsigned int numTreinos = getNumTreinos(); for(unsigned int i = 0 ; i< numTreinos; i++) { unsigned int treino = getTreino(i); if(treino <=10) { custo += 10; }else if(treino > 10 && treino < 20) { custo += 15; }else { custo += 25; } } return custo; } Tarifario* Apressado::duplica() const { return new Apressado(*this); } string Apressado::getAsString() const { ostringstream oss; oss << " apressado " << endl; return oss.str(); } //cliente genérico, significa que é uma classe base de uma hierarquia class Cliente { string nome; unsigned int bi; //ou string Tarifario* tarifario; //aponta para um qualquer tipo de tarifario que exista int horarioInicio; //para impedir a copia e a atribuição de objectos //se for solicitado era: //Cliente(const Cliente& cli) = delete; //Cliente& operator=(const Cliente& cli) = delete; public: //nao podem existir clientes sem tarifario, é necessário o construtor Cliente(const string& nome, unsigned long bi, Tarifario* const tarifario); //destrutor para apagar tarifario virtual ~Cliente(); void iniciaTreino(int hora); void terminaTreino(int hora); unsigned int paga(); //pergunta exame //um método generico que não pode ser já implementado, é uma função abstracta, virtual e =0 //e vao ter codigo nas classes derivadas virtual void reageEntrada(Ginasio* g) = 0; virtual void reageSaida(Ginasio* g) = 0; void mudaTarifario(Tarifario* tarifario); unsigned int getBi()const; bool estarATreinar()const; //a c.5) Cliente(const Cliente& cli); Cliente& operator=(const Cliente& cli); virtual string getAsString()const; //tem que ser virtual pura (cliente é abstracta) virtual Cliente* duplica()const = 0; }; Cliente::Cliente(const string& nome, unsigned long bi, Tarifario* const tarifario) : nome(nome), bi(bi), tarifario(tarifario) { horarioInicio = -1; //nao esta a treinar quando é criado } Cliente::~Cliente() { delete tarifario; cout << "~Cliente" << endl; } void Cliente::iniciaTreino(int hora) { if(horarioInicio==-1) { horarioInicio = hora; } } void Cliente::terminaTreino(int hora) { if(horarioInicio != -1) { tarifario->acrescentaTreino(hora - horarioInicio); horarioInicio = -1; } } unsigned Cliente::paga() { //quem calcula é o tarifario, e o cliente tem return tarifario->calculaPagamentoEApagaTreinos(); } void Cliente::mudaTarifario(Tarifario* tarifario) { if(this->tarifario != nullptr) { //so muda se o novo tarifario existir mesmo //apaga tarifario anterior (deixa de servir) //(obs: os treinos do antigo tarifario ficaram //por pagar deixa-se pagar antes de mudar de tarifario delete this->tarifario; this->tarifario = tarifario; } } bool Cliente::estarATreinar() const { return horarioInicio != -1; } Cliente::Cliente(const Cliente& cli) { //efectua pre inicilização para compatibilizar com o operador //neste caso basta colocar o ponteiro tarifario num estacio inciial coerente tarifario = nullptr; //usa operador atribuicao *this = cli; } Cliente& Cliente::operator=(const Cliente& cli) { //testa a auto atribuição para evitar trabalho desnecessario //e libertacao do tarifario da origem da copia (que coincide com o destino) if(this == &cli) { return *this; } //liberta recursos atuais do objecto da atribuicao //neste caso so o objeto tarifario delete tarifario; //cria/copia recursos (iguais ao) do outro objeto //no caso do tarifario, pode ser de qualquer tipo //usar o new nao e viavel ( nao se sabe o tipo ) //o tarifario que se duplique //tem que ser pela via do "duplica" (implementar no tarifario e derivadas) nome = cli.nome; bi = cli.bi; horarioInicio = cli.horarioInicio; tarifario = cli.tarifario->duplica(); return *this; } string Cliente::getAsString() const { ostringstream oss; oss << nome << " - bi: " << bi; return oss.str(); } //classe concreta //e) class Sociavel: public Cliente { public: Sociavel(const string& nome, const unsigned long bi, Tarifario* const tarifario); void reageEntrada(Ginasio* g) override; void reageSaida(Ginasio* g) override; Cliente* duplica() const override; string getAsString() const override; }; Sociavel::Sociavel(const string& nome, const unsigned long bi, Tarifario* const tarifario) : Cliente(nome, bi, tarifario) { } void Sociavel::reageEntrada(Ginasio* g) { //nao faz nada } void Sociavel::reageSaida(Ginasio* g) { if(g->getNumClientesATreinar()==1) //se for só ele { g->saiClienteDoTreino(getBi()); } } Cliente* Sociavel::duplica() const { return new Sociavel(*this); } string Sociavel::getAsString() const { ostringstream oss; oss << "sociavel - " << Cliente::getAsString(); return oss.str(); } //os clientes pertencem ao ginasio //qd se destroi o ginasio o cliente tambem serão class Ginasio { //clientes de diversos tipos //vector de ponteiros para a classe base //* para validar o polimorfismo vector<Cliente*> clientes; unsigned int relogio; unsigned int pesquisaClienteDadoBi(unsigned int bi); public: void avancaRelogio(int tempo); bool acrescentaCliente(Cliente* c); unsigned int paga(unsigned int bi); void entraClienteNoTreino(unsigned int bi); void saiClienteDoTreino(unsigned int bi); void removeCliente(unsigned int bi); Ginasio(); ~Ginasio(); unsigned int getNumClientesATreinar()const; bool saiClienteDoTreino(int s); Ginasio& operator=(const Ginasio& orig); Ginasio(const Ginasio& orig); string getAsString() const; }; unsigned Ginasio::pesquisaClienteDadoBi(unsigned bi) { for(unsigned int i = 0; i<clientes.size(); i++) { if(bi == clientes[i]->getBi()) { return i; } } return -1; } void Ginasio::avancaRelogio(int tempo) { relogio += tempo; } bool Ginasio::acrescentaCliente(Cliente* c) { if(c == nullptr) { return false; } //se existem clientes com o mesmo bi if(pesquisaClienteDadoBi(c->getBi()) != -1) { return false; } clientes.push_back(c->duplica()); return true; } unsigned Ginasio::paga(unsigned bi) { //procura cliente. sera devolvida a posicao no vector unsigned int indice = pesquisaClienteDadoBi(bi); if(indice != -1) { //se encontrou.. return clientes[indice]->paga(); } return 0; } void Ginasio::entraClienteNoTreino(unsigned bi) { unsigned int indice = pesquisaClienteDadoBi(bi); if(indice == -1) { return; } //verificar se o cliente ainda não estava a treinar if(clientes[indice]->estarATreinar() == true) { return; } //avisa os outros clientes a treinar que entrou na sala for(unsigned int i=0; i< clientes.size(); i++) { if(clientes[i]->estarATreinar()){ clientes[i]->reageEntrada(this); } } //o cliente inicia o treino clientes[indice]->iniciaTreino(relogio); } void Ginasio::saiClienteDoTreino(unsigned bi) { unsigned int indice = pesquisaClienteDadoBi(bi); if(indice == -1) { return; } //cliente pode nao estar a treinar if(clientes[indice]->estarATreinar() == false) { return; } //primeiro tira da sala de treino e so depois avisa os que ficaram que saiu um clientes[indice]->terminaTreino(relogio); //avisa os outros clientes a treinar que saiu um da sala for(unsigned int i = 0; i< clientes.size(); i++) { if(clientes[i]->estarATreinar()) { clientes[i]->reageSaida(this); } } } void Ginasio::removeCliente(unsigned bi) { //procura o cliente na base de dados de clientes unsigned int indice = pesquisaClienteDadoBi(bi); if(indice == -1) { return; } //encontrou manda sair do treino saiClienteDoTreino(bi); //antes de ir embora pagar a conta clientes[indice]->paga(); //apaga, os clientes pertencem ao ginasio delete clientes[indice]; //retira do vector o ponteiro para memoria já libertada clientes.erase(clientes.begin() + indice); } Ginasio::Ginasio():relogio(0) { } Ginasio::~Ginasio() { //apaga as fichas for(unsigned int i = 0; i< clientes.size(); i++) { delete clientes[i]; //os clientes pertencem ao ginasio } } unsigned Ginasio::getNumClientesATreinar() const { unsigned int n = 0; for(unsigned int i = 0; i<clientes.size();i++) { if(clientes[i]->estarATreinar()) { n++; } } return n; } Ginasio& Ginasio::operator=(const Ginasio& orig) { //testar a auto atribuição se sao o mesmo objecto, "ja nao sao iguais" //nao se libertam recursos dos destino da atribuição porque coincide com a origem if(this == &orig) { return *this; } //apaga o conceudo ja existente no "alvo" da atribuicao (*this) for(unsigned int i = 0; i< clientes.size(); i++) { delete clientes[i]; //apaga mesmo os clientes (eram do ginasio) } //os clientes foram apagados, mas os ponteiros clientes.clear(); //ainda estão no vector, o qual deve ser esvaziado for(unsigned int i = 0; i<clientes.size(); i++) { //independentemente do tipo do cliente, duplica clientes.push_back(orig.clientes[i]->duplica()); } //copiar o valor do relgio relogio = orig.relogio; return *this; } Ginasio::Ginasio(const Ginasio& orig) { //nao é preciso pre inicialização explicita porque o vector e //inicializado automaticamente com o seu construtor por omissao //um avez que nao foi usado nenhum outro na lista de inicializaca deste construtor //e que o deixa vazio e pronto a usar. noutros casos poderia ser necessario algo aqui *this = orig; } string Ginasio::getAsString() const { ostringstream oss; oss << "\n ginasio" << "(tempo " << relogio << ")" << "clientes: " << endl; for(unsigned int i = 0 ; i < clientes.size(); i++) { oss << clientes[i]->getAsString() << endl; } oss << "\na treinar " << endl; for(unsigned int i = 0; i< clientes.size(); i++) { if(clientes[i]->estarATreinar()) { oss << clientes[i]->getAsString() << endl; } } return oss.str(); } int main() { cout << "\nfim do main" << endl; return 1; } }
………..ficha7, exercicio5 (a24)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class Tagarela; class FalaPouco; //classe base, abstracta, relação de class Tarifario { //nao existem dados, quem tem é o Cartao //nao tem informação public: //só tem funções Tarifario() = default; virtual float calculaCustoChamada(int s) const = 0; //virtual e =0 virtual float fazCarregamento(float quantia) const = 0; virtual bool autorizaChamada(int saldo) const = 0; virtual string getNome()const = 0; virtual Tarifario* duplica() const = 0; //por causa do operador atribuição na classe rede static Tarifario* fabrica(string tipo); //fabrica de tarifarios, para os arrumar virtual string getAsString() const; }; Tarifario* Tarifario::fabrica(string tipo) { if(tipo=="Tagarela") { return new Tagarela; }else if(tipo == "FalaPouco") { return new FalaPouco; }else { return nullptr; } } string Tarifario::getAsString() const { ostringstream oss; oss << "tarifario: " << endl; return oss.str(); } //classe derivada class Tagarela : public Tarifario { //existem constantes desta classe static const float PRIMEIRO_MINUTO; static const float MINUTOS_SEGUINTES; static const float BONUS; static const float BASE; public: float calculaCustoChamada(int s) const override; float fazCarregamento(float quantia) const override; bool autorizaChamada(int saldo) const override; string getNome() const override; Tarifario* duplica() const override; string getAsString() const override; }; //concretizar as constantes const float Tagarela::PRIMEIRO_MINUTO = 0.02f; const float Tagarela::BONUS = 5.0f; const float Tagarela::BASE = 25.0f; const float Tagarela::MINUTOS_SEGUINTES = 0.02f; float Tagarela::calculaCustoChamada(int s) const { if(s<=0) //saldo { return 0; } float aux_custo = PRIMEIRO_MINUTO; if(s>60) { aux_custo += MINUTOS_SEGUINTES * (s - 60) / 60.0; } return aux_custo; } float Tagarela::fazCarregamento(float quantia) const { if(quantia<BASE) { return 0; }else if(quantia>=2*BASE) { quantia += BONUS; } return quantia; } bool Tagarela::autorizaChamada(int saldo) const { return saldo >= -PRIMEIRO_MINUTO; } string Tagarela::getNome() const { return "Tagarela"; } Tarifario* Tagarela::duplica() const { return new Tagarela(*this); } string Tagarela::getAsString() const { ostringstream oss; oss << "tagarela: " << endl; return oss.str(); } //classe derivada class FalaPouco : public Tarifario { static const float TODOS_MINUTOS; static const float CARREGAMENTOS; static const float BONUS_CARREGAMENTOS; public: float calculaCustoChamada(int s) const override; float fazCarregamento(float quantia) const override; bool autorizaChamada(int saldo) const override; string getNome() const override; Tarifario* duplica() const override; string getAsString() const override; }; //concretizar as constantes const float FalaPouco::TODOS_MINUTOS = 0.25f; const float FalaPouco::CARREGAMENTOS = 10.0f; const float FalaPouco::BONUS_CARREGAMENTOS = 0.2f; float FalaPouco::calculaCustoChamada(int s) const { if(s<0) { return 0; } return TODOS_MINUTOS * s / 60.0; } float FalaPouco::fazCarregamento(float quantia) const { if(quantia<CARREGAMENTOS) { return 0; } int n = rand() % 10; //de 0 a 9 if(n<2) //<2 é 20% { quantia *= 2; } return quantia; } bool FalaPouco::autorizaChamada(int saldo) const { //ou talvez meu if(saldo<=(TODOS_MINUTOS*10)) { return false; } return true; //ou //return saldo >= - TODOS_MINUTOS * 10; } string FalaPouco::getNome() const { return "FalaPouco"; } Tarifario* FalaPouco::duplica() const { return new FalaPouco(*this); } string FalaPouco::getAsString() const { ostringstream oss; oss << "falapouco" << endl; return oss.str(); } class Cartao { long numero; float saldo; Tarifario* tarifario; public: Cartao(const long numero, Tarifario* const tarifario); virtual ~Cartao(); long getNumero() const; float getSaldo() const; string getNomeTarifario() const; void setTarifario(Tarifario* const tarifario0); bool autorizaChamada()const; void registaChamada(int saldo); bool fazCarregamento(float quantia); string getAsString()const; }; bool operator==(const Cartao& orig1, const Cartao& orig2); //para dois tarifários iguais!! Cartao::Cartao(const long numero0, Tarifario* const tarifario0):numero(numero0), tarifario(tarifario0), saldo(0){} long Cartao::getNumero() const { return numero; } float Cartao::getSaldo() const { return saldo; } string Cartao::getNomeTarifario() const { return tarifario->getNome(); } void Cartao::setTarifario(Tarifario* const tarifario0) { this->tarifario = tarifario0; } bool Cartao::autorizaChamada() const { //função polimorfica, conforme o tarifario return tarifario->autorizaChamada(saldo); } void Cartao::registaChamada(int saldo0) { //função polimorfica, conforme o tarifario float aux_custo = tarifario->calculaCustoChamada(saldo0); saldo = -aux_custo; } bool Cartao::fazCarregamento(float quantia0) { //função polimorfica, conforme o tarifario float aux_carregamento = tarifario->fazCarregamento(quantia0); saldo += aux_carregamento; return aux_carregamento > 0; } string Cartao::getAsString() const { ostringstream oss; oss << "(numero " << numero << " saldo: " << saldo << " tarifario " << tarifario->getNome() << ")"; return oss.str(); } bool operator==(const Cartao& orig1, const Cartao& orig2) { return orig1.getNumero() == orig2.getNumero(); } class Rede { string nome; //tarifarios é uma classe abstracta //logo não pode haver objectos de uma classe abstracta //vector <Tarifario> tarifarios; //e queremos ponteiros para a classe base, e uma coleção polimorfica vector <Tarifario*> tarifarios; //cartoes não tem derivadas //e podia ser vector <Cartao> cartoes; vector <Cartao*> cartoes; Tarifario* procuraTarifario(const string& nome) const; public: Rede(const string& nome0); //rede composição: qd a rede deixar de existir os tarifários tb deixam Rede(const Rede& ob); Rede & operator=(const Rede& ob); //tem uma dificuldade ~Rede(); void setNome(const string& nome0); bool acrescentaTarifario(const string& nomeTarifario); bool acrescentaCartao(long num, const string& nomeTarifa); int procuraCartao(long i)const; bool removeCartao(long i); bool registaChamada(long numero, int duracao); bool fazCarregamento(long numero, float quantia); bool autorizaChamada(long n)const; string getAsString() const; }; ostream& operator<<(ostream& saida, const Rede& rede); Tarifario* Rede::procuraTarifario(const string& nome) const { for(Tarifario *t : tarifarios) { if(t->getNome() == nome) { return t; } } return nullptr; } Rede::Rede(const string& nome0):nome(nome0) { } Rede::Rede(const Rede& ob) { *this = ob; } Rede& Rede::operator=(const Rede& ob) //immportante, complicado { //prevenção da auto atribuicao if(this == &ob) { return *this; } //libertar mem. dinamica apontada pelo primeiro membro da atribuicao //e esvaziar os vectores for(Tarifario *t: tarifarios) { delete t; } tarifarios.clear(); for(Cartao *c: cartoes) { delete c; } cartoes.clear(); //copiar os dados do segundo membro da atribuição nome = ob.nome; for(Tarifario * t: ob.tarifarios) { tarifarios.push_back(t->duplica()); } //depois de todos os tarifários do primeiro membro da atribuição copiados for(Cartao *c: ob.cartoes) { Cartao* novo = new Cartao(*c); //cópia do cartao //que aponta para um tarifario do segundo membro da atribuicao string nomeTarifario = c->getNomeTarifario(); //identifica do seu tarifario //procura tarifario com nomeTarifario no vector do primeiro membro da atribuicao Tarifario* t = procuraTarifario(nomeTarifario); novo->setTarifario(t); cartoes.push_back(novo); } } Rede::~Rede() { //porque sao vectres de ponteiros para objectos dinamicos for(Tarifario *t: tarifarios) { delete t; } for(Cartao *c: cartoes) { delete c; } //e o que o destrutor libetra //o construtor por copia e o operador atribuicao tem que duplicar cout << "destr da rede " << nome << endl; } void Rede::setNome(const string& nome0) { this->nome = nome0; } bool Rede::acrescentaTarifario(const string& nomeTarifario) { Tarifario* tarifario0 = procuraTarifario(nomeTarifario); if(tarifario0 != nullptr) { return false; } Tarifario* novoTarifario = Tarifario::fabrica(nomeTarifario); if(novoTarifario == nullptr) { return false; } tarifarios.push_back(novoTarifario); return true; } bool Rede::acrescentaCartao(long num, const string& nomeTarifa) { if(procuraCartao(num) != -1) { return false; } Tarifario* tf = procuraTarifario(nomeTarifa); if(tf == nullptr) { return false; } cartoes.push_back(new Cartao(num, tf)); return true; } int Rede::procuraCartao(long i) const { for(unsigned int indice = 0; indice < cartoes.size(); indice++) { if(cartoes[indice]->getNumero() == i) { return indice; } } return -1; } bool Rede::removeCartao(long i) { int aux = procuraCartao(i); if(aux == -1) { return false; } //relação de composição delete cartoes[aux]; //libetra o objecto cartoes.erase(cartoes.begin() + aux);//tira o ponteiro do vector return true; } bool Rede::registaChamada(long numero, int duracao) { int aux = procuraCartao(numero); if(numero == -1) { return false; } cartoes[aux]->registaChamada(duracao); return true; } bool Rede::fazCarregamento(long numero, float quantia) { int aux = procuraCartao(numero); if(aux== -1) { return false; } return cartoes[aux]->fazCarregamento(quantia); } bool Rede::autorizaChamada(long n) const { int aux = procuraCartao(n); if(aux == -1) { return false; } return cartoes[aux]->autorizaChamada(); } string Rede::getAsString() const { ostringstream oss; oss << "\nRede: " << nome << endl << " tarifarios: "; for(Tarifario *t : tarifarios) { oss << t->getNome() << "\t"; } oss << "\nCartoes: " << endl; for(Cartao *c : cartoes) { oss << c->getAsString() << endl; } return oss.str(); } ostream& operator<<(ostream& saida, const Rede& rede) { saida << rede.getAsString(); return saida; } int main(){ Rede* rede = new Rede("meo"); rede->acrescentaTarifario("Tagarela"); rede->acrescentaTarifario("FalaPouco"); rede->acrescentaCartao(10, "Tagarela"); rede->acrescentaCartao(20, "FalaPouco"); cout << rede->getAsString(); rede->removeCartao(10); cout << rede->getAsString(); delete rede; cout << "\nfim do main" << endl; return 0; }
classes cujos objectos interagem de forma bidirecional
………..ficha6, exercicio1 (a18)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class Aquario; class Peixe { string nomeEspecie; string cor; int peso; int numSerie; int estadio; //0, normal, 1, emagrece, 2,3,4,5 nao come, 6 morre static int sequencia; //constantes iguais para todos: static const int LIMITE_PESO = 50; static const int INICIAL_PESO = 10; public: Peixe(string nomeEspecie0, string cor0="cinzento"); //posso e controlo total: //composição: destrutor a lierbtar os peixes, construtor pro copia e operador atribuição a duplicar aquilo que o destrutor destroi string getNomeEspecie() const; void setNomeEspecie(const string& nome_especie); string getCor() const; void setCor(const string& cor); int getPeso() const; void setPeso(const int peso); int getNumSerie() const; void setNumSerie(const int num_serie); int getEstadio() const; void setEstadio(const int estadio); bool isVivo()const; void alimentar(int quant, Aquario* aquario); string getAsString()const; }; Peixe::Peixe(string nomeEspecie0, string cor0):nomeEspecie(nomeEspecie0), cor(cor0), peso(INICIAL_PESO), numSerie(++sequencia), estadio(0) { } string Peixe::getNomeEspecie() const { return nomeEspecie; } void Peixe::setNomeEspecie(const string& nome_especie) { nomeEspecie = nome_especie; } string Peixe::getCor() const { return cor; } void Peixe::setCor(const string& cor) { this->cor = cor; } int Peixe::getPeso() const { return peso; } void Peixe::setPeso(const int peso) { this->peso = peso; } int Peixe::getNumSerie() const { return numSerie; } void Peixe::setNumSerie(const int num_serie) { numSerie = num_serie; } int Peixe::getEstadio() const { return estadio; } void Peixe::setEstadio(const int estadio) { this->estadio = estadio; } bool Peixe::isVivo() const { return getEstadio() <= 5; } void Peixe::alimentar(int quant, Aquario* aquario) { if(quant <= 0|| aquario == nullptr) { return; } if(estadio >0 && estadio<=5) { ++estadio; return; } if(estadio>5) { return; } peso += quant; if(peso> LIMITE_PESO) { int probabilidade = rand() % 100; if(probabilidade < 50) { peso = LIMITE_PESO - INICIAL_PESO; aquario->nasceNovoPeixe(nomeEspecie, cor); }else { peso /= 2; //reduzido a metade estadio = 1; } } } string Peixe::getAsString() const { ostringstream oss; oss << "\nPeixe: " << nomeEspecie << "\ncor: " << cor << "\npeso: " << peso << "\nnumSerie: " << numSerie << (estadio <= 5 ? " vivo" : " morto") << " estadio " << estadio << endl; return oss.str(); } int Peixe::sequencia = 499; //num. para cada peixe //relação bidirecional entre o Peixe e o Aquario //include no CPP e declaração da classe no h class Aquario { //tem uma colecção de peixes vector<Peixe*> peixes; vector<Peixe*> novos; //o peixe usa para colocar os novos void eliminarMortos(); void deslocaNovoParaAquario(); int pesquisaPeixe(int numSerie0)const; public: Aquario() = default; //os dois vectores são construidos por omissão, isto é ficam vazios na construção //e porque não existe nenhum ponteirp primitivo na classe //e no construtor por copia fica mais simples virtual ~Aquario(); //quando um peixei e colocado no aquario, este assume //a sua posse e controlo total: Aquario(const Aquario& orig); //construtor por copia Aquario& operator=(const Aquario & orig); //operador atribuição void removePeixe(int numSerie); void alimentar(int quantidade0); void nasceNovoPeixe(string especie0, string cor0); bool acrescentaPeixeDoExterior(Peixe* peixe); string getAsString()const; }; void Aquario::alimentar(int quantidade0) { for(Peixe * p : peixes) { p->alimentar(quantidade0, this); //mt cuidado com isto, usa o vector<Peixe*> novos; } eliminarMortos(); deslocaNovoParaAquario(); //sair do novo para o aquario normal } void Aquario::nasceNovoPeixe(string especie0, string cor0) { novos.push_back(new Peixe(especie0, cor0)); } bool Aquario::acrescentaPeixeDoExterior(Peixe* peixe) { if (peixe == nullptr){ return false; } //se o peixe estiver no aquario if(pesquisaPeixe(peixe->getNumSerie())!= -1) { return false; } //se ainda nao esta no aquario //criar um novo em mem dinamica, com new Peixe* p = new Peixe(*peixe); //faço copia local peixes.push_back(p); return true; } string Aquario::getAsString() const { ostringstream oss; oss << "\nAquario; " << endl; for(Peixe * p: peixes) { oss << p->getAsString(); } return oss.str(); } int Aquario::pesquisaPeixe(int numSerie0)const { for(int i=0; i < peixes.size(); i++) { if(peixes[i]->getNumSerie() == numSerie0) { return i; } } return -1; } void Aquario::eliminarMortos() { //formula //ou //for(vector<Peixe*>::iterator it = peixes.begin() ; it != peixes.end(); ) //ou for(auto it = peixes.begin(); it != peixes.end();) { if(!(*it)->isVivo()) { delete (*it); it = peixes.erase(it); }else { ++it; } } } void Aquario::deslocaNovoParaAquario() { for(Peixe * p: peixes) { peixes.push_back(p); } novos.clear(); } Aquario::~Aquario() { for(Peixe * p: peixes) { delete p; } for(Peixe * p: novos) { delete p; } cout << "\n~Aquario()" << endl; } Aquario::Aquario(const Aquario& orig) { *this = orig; } Aquario& Aquario::operator=(const Aquario& orig) { //prevenção da auto atribuição if(this == &orig) { return *this; } //libtertar mem dina velha for(Peixe * p : peixes) { delete p; } peixes.clear(); //copiar a infor de orig, duplicando os objectos dinamicos Peixe for(Peixe * p: orig.peixes){ peixes.push_back(new Peixe(*p)); } for(Peixe * p: orig.peixes) { novos.push_back(new Peixe(*p)); } return *this; } void Aquario::removePeixe(int numSerie) { int qual = pesquisaPeixe(numSerie); if(qual == -1) { return; } delete peixes[qual]; peixes.erase(peixes.begin() + qual); } int main() { Peixe a("Robalo", "branco"); Peixe b("Pescada"); Peixe c("Salmao"); Aquario* aq1 = new Aquario; aq1->acrescentaPeixeDoExterior(&a); aq1->acrescentaPeixeDoExterior(&b); aq1->acrescentaPeixeDoExterior(&c); cout << aq1->getAsString() << endl; // peso 10 aq1->alimentar(20); cout << aq1->getAsString() << endl; // peso 30 aq1->alimentar(30); cout << aq1->getAsString() << endl; // peso 60 e consequencias for (int i = 0; i < 6; ++i) { aq1->alimentar(1); cout << aq1->getAsString() << endl; } aq1->removePeixe(500); aq1->removePeixe(500); cout << aq1->getAsString() << endl; /// testa mem din Aquario* aq2 = new Aquario; aq2->acrescentaPeixeDoExterior(&a); aq2->acrescentaPeixeDoExterior(&a); aq2->acrescentaPeixeDoExterior(&a); cout << aq2->getAsString() << endl; *aq2 = *aq1; delete aq1; cout << " aq1:\n"; cout << " aq2:\n"; cout << aq2->getAsString() << endl; Aquario* aq3 = new Aquario(*aq2); delete aq2; cout << aq3->getAsString() << endl; delete aq3; cout << "\nfim do main" << endl; return 1; }
objetos dinâmicos, matrizes dinâmicas, classes com construtores por cópia, operador de atribuição e destrutor
………..ficha5, exercicio1 (a15)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; class MSG { string mensagem; public: // MSG(const string & s); MSG(const string& s="por omissao"); //d) ~MSG(); }; MSG::MSG(const string& s):mensagem(s) { cout << "ola " << mensagem << "\n"; } MSG::~MSG() { cout << "adeus " << mensagem << endl; } MSG * func(string s) { MSG* p = new MSG(s); cout << "\n func: criado um objecto " << s << endl; return p; //retornamos uma copia, que é o valor do endereço } int main() { //a) //MSG * a = new MSG("AAAA"); //MSG * b = new MSG("BBBB"); ////para destruir o new só aplicando o delete //cout << "\ndestruir.."; //delete a; //destroi apenas o objecto dinamico, não o ponteiro //cout << "\ndestruir.."; //delete b; // ////um objecto que nao é dinamico //MSG c("CCCC"); //a = new MSG("outro"); //o ponteiro a continua disponivel! //delete a; //c usar a func(a,b) //MSG* a = func("AAAA"); //MSG* b = func("BBBB"); //delete a; //delete b; //d) MSG* v = new MSG[3]; // com este construtor MSG(const string & s); nao funciona //só funciona com construtor por omissão MSG* v0 = new MSG[2]{ {"primeiro"}, {"segundo"} }; //e) delete [] v; delete [] v0; //não se pode libertar espaço do MSG* v = new MSG[3]; //quando se liberta é todos de uma vez cout << "\nfim do main" << endl; return 0; }
………..ficha5, exercicio2 (a15, a16)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; /* +---------------------------------------- + -------------------------- - + | operador | membro / não membro | +---------------------------------------- + -------------------------- - + | todos os operadores unários p.e: ++a, a++ | membro | | = () -> ->* | têm sempre que ser membro | | += -= /= *= &= |= %= >>= <<= | membro | | todos os restantes operadores binários, ==,<<... | globais | +--- */ class ABC { char* p; public: ABC(const char* s); ~ABC(); //c const char* getConteudo() const; //c) construtor por copia ABC(const ABC& ob); //c) operador atribuição ABC& operator=(const ABC & ob); }; ABC::ABC(const char* s) { p = new char[strlen(s) + 1]; //construtor cria, composição strcpy(p, s); cout << "\nconstruindo .." << p << endl; } ABC::~ABC() { cout << "\ndestruindo .." << p << endl; delete[] p; } const char* ABC::getConteudo() const { return p; } //ABC::ABC(const ABC& ob) //c) versão um //{ // p = nullptr; // //ver se estamos a copiar uma string vazia // if(ob.p == nullptr || strlen(ob.p)==0) // { // return; // } // //alocar espaco para copiar a string da origem da copia // p = new char[strlen(ob.p) + 1]; // //copiar para o proprio espaco // strcpy(p,ob.p); // // cout << "\nconstrutor por copia: " << p << endl; //} ABC& ABC::operator=(const ABC& ob) //c) { //operador atribuição tem: //prevenção da auto atribuição if (this == &ob) { return *this; } //libertacao de memoria apontada pelo prim membro de atribuição delete[]p; //ponteiro que tem lixo //limpar objecto p = nullptr; //se estamos a copiar uma string vazia if(ob.p == nullptr || strlen(ob.p)==0) { return *this; } //alocar espaco para copiar a string do segundo membro p = new char[strlen(ob.p) + 1]; //copiar a string do seg membro para um espaco proprio strcpy(this->p, ob.p); cout << "\nOperador atribuicao "; return *this; } //c) alternativa versao dois //construtor por copia à custa do operador atribuição ABC::ABC(const ABC& ob) { p = nullptr; *this = ob; //ao fazer esta atribuição chama.se o operator= cout << "\n CCop " << (p != nullptr ? p : nullptr) << " - " << ob.p << endl; } //b void gastaMemoria() { ABC temp("\nTexto temporario que ocupa espaco"); } //c) void func(ABC a) { //o a é passado por valor //o a é criado pro copia de y, com o construtor por cópia //o construtor automatico, faz copia de membro a membro } void func() { ABC a("aaa"); ABC b("bbb"); a = b; } int main() { //b //for(unsigned int i = 0; i<10 ; i++) //{ // gastaMemoria(); //} //c //a classe não tem construtor por cópia //a classe não tem operador de atribuição //apos o construtor por copia ABC y("ola y"); cout << "conteudo do y: " << y.getConteudo() << endl; func(y); cout << "conteudo do y: " << y.getConteudo() << endl; //apos o operador de atribuição e a versao dois func(); cout << "\nfim do main" << endl; }
………..ficha5, exercicio5 (a16)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; //versão matriz dinamica class Pessoa { string nome; long bi; long nif; void setBi(long b); public: Pessoa() = delete; Pessoa(const string& s, long b, long n); string getNome()const; void setNome(const string& n); long getBi()const; long getNif()const; void setNif(long t); string getAsString()const; }; Pessoa::Pessoa(const string& s, long b, long n) : nome(s), bi(b), nif(n) { } string Pessoa::getNome()const { return nome; } void Pessoa::setNome(const string& n) { nome = n; } long Pessoa::getBi()const { return bi; } void Pessoa::setBi(long b) { bi = b; } long Pessoa::getNif()const { return nif; } void Pessoa::setNif(long t) { nif = t; } string Pessoa::getAsString()const { ostringstream oss; oss << "\nNome: " << nome << "\nBi: " << bi << "\nNif: " << nif << endl; return oss.str(); } class Clube { Pessoa** socios; //vector <Pessoa *> socios; int tam; public: Clube(int t); ~Clube(); void setMembroDoclube(Pessoa* p, int pos); //construtor por copia Clube(const Clube& ob); //operador atribuição Clube & operator=(const Clube & ob); string getAsString() const; }; Clube::Clube(int t) { tam = t > 0 ? t : 0; if(tam == 0) { socios = nullptr; return; } socios = new Pessoa * [tam]; //tam, é um array tam, de tam Pessoa *. if (socios == nullptr) { tam = 0; return; } for (unsigned int i = 0; i < tam; i++) socios[i] = nullptr; //tudo igual a nulltpr } Clube::~Clube() { delete[]socios; //liberta o array dinamico dos ponteiros //nao existe o ciclo for para deletar as pessoas //ninguem toca nas pessoas } //v1 //void Clube::setMembroDoclube(Pessoa* p, int pos) //{ // socios[pos] = p; //socios 0 = endereço de p //} //v2 void Clube::setMembroDoclube(Pessoa* p, int pos) { if(pos >= 0 && pos < tam && socios[pos] == nullptr) { socios[pos] = p; } } Clube::Clube(const Clube& ob) { //limpar os ponteiros aos quais o operador atribuicao vai fazer delete socios = nullptr; tam = 0; //aplicar o operador atribuição *this = ob; } Clube & Clube::operator=(const Clube& ob) { //prevenção da auto atribuição if(this == &ob) { return *this; } //limpar memoria velha delete[] socios; //a seguir é necessário o nullptr socios = nullptr; tam = 0; //se a origem da copia for um clube sem socios if(ob.socios == nullptr || ob.tam ==0 ) { return * this; } //se a origem da copia for um clube com socios socios = new Pessoa * [ob.tam]; //copiar o tam tam = ob.tam; //copiar os ponteiros for(unsigned int i = 0; i < tam; i++) { //nao se duplicam os cosios porque a relacao entre clube e socios e de agregacao //o destrutor tambem nao os destroi socios[i] = ob.socios[i]; } return *this; } string Clube::getAsString() const { ostringstream oss; oss << "\nTam: " << tam << endl; for (unsigned int i = 0; i < tam; i++) { if (socios[i] != nullptr) { oss << socios[i]->getAsString() << endl; } } return oss.str(); } int main() { //agregacao entre o clube e as pessoas //relação entre o clube e o array de ponteiros é de composição Pessoa a("aaa",1,111), b("bbb", 2, 222); Clube* clube1 = new Clube(50); clube1->setMembroDoclube(&a, 0); clube1->setMembroDoclube(&b, 1); cout << "Clube1: " << clube1->getAsString(); Pessoa c("ccc", 3, 333), d("ddd", 4, 444); Clube* clube2 = new Clube(10); clube2->setMembroDoclube(&a, 0); clube2->setMembroDoclube(&b, 1); clube2->setMembroDoclube(&c, 2); clube2->setMembroDoclube(&d, 3); cout << "Clube2: " << clube2->getAsString(); *clube1 = *clube2; //atribuir os apontados delete clube2; cout << "Clube2 depois: " << clube1->getAsString(); //copia do apontado Clube clube3(*clube1); cout << "Clube3 depois: " << clube3.getAsString(); cout << "\nfim do main" << endl; }
………..ficha5, exercicio5 (a16,)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; //versão vector class Pessoa { string nome; long bi; long nif; void setBi(long b); public: Pessoa() = delete; Pessoa(const string& s, long b, long n); string getNome()const; void setNome(const string& n); long getBi()const; long getNif()const; void setNif(long t); string getAsString()const; }; Pessoa::Pessoa(const string& s, long b, long n) : nome(s), bi(b), nif(n) { } string Pessoa::getNome()const { return nome; } void Pessoa::setNome(const string& n) { nome = n; } long Pessoa::getBi()const { return bi; } void Pessoa::setBi(long b) { bi = b; } long Pessoa::getNif()const { return nif; } void Pessoa::setNif(long t) { nif = t; } string Pessoa::getAsString()const { ostringstream oss; oss << "\nNome: " << nome << "\nBi: " << bi << "\nNif: " << nif << endl; return oss.str(); } class Clube { //Pessoa** socios; vector <Pessoa *> socios; int tam; public: Clube(int t); //~Clube(); void setMembroDoclube(Pessoa* p, int pos); //construtor por copia //Clube(const Clube& ob); //operador atribuição //Clube& operator=(const Clube& ob); //Nesta versao não é preciso construtor por copia, atribuição e destrutor //Por nesta classe so usarmos ponteiros, e não ciramos nenhuma memoria dinamica, logo não existe necessidade de cópiar nada string getAsString() const; }; //Clube::Clube(int t) //{ // tam = t > 0 ? t : 0; // if (tam == 0) // { // socios = nullptr; // return; // } // socios = new Pessoa * [tam]; //tam, é um array tam, de tam Pessoa *. // if (socios == nullptr) { // tam = 0; // return; // } // for (unsigned int i = 0; i < tam; i++) // socios[i] = nullptr; //tudo igual a nulltpr //} Clube::Clube(int t) { for (unsigned int i = 0; i < t; i++) { socios.push_back(NULL); } } //v1 //Clube::~Clube() //{ // delete[]socios; //liberta o array dinamico dos ponteiros // //nao existe o ciclo for para deletar as pessoas // //ninguem toca nas pessoas //} //v1 //void Clube::setMembroDoclube(Pessoa* p, int pos) //{ // socios[pos] = p; //socios 0 = endereço de p //} //v2 //void Clube::setMembroDoclube(Pessoa* p, int pos) //{ // if (pos >= 0 && pos < tam && socios[pos] == nullptr) // { // socios[pos] = p; // } //} //v3 vestores void Clube::setMembroDoclube(Pessoa* p, int pos) { if (pos >= 0 && pos < socios.size() && socios[pos] == NULL) { socios[pos] = p; // Notar que o obj. Pessoa e visto pelo Clube } } //Clube::Clube(const Clube& ob) //{ // //limpar os ponteiros aos quais o operador atribuicao vai fazer delete // socios = nullptr; // tam = 0; // //aplicar o operador atribuição // *this = ob; //} // //Clube& Clube::operator=(const Clube& ob) //{ // //prevenção da auto atribuição // if (this == &ob) // { // return *this; // } // //limpar memoria velha // delete[] socios; //a seguir é necessário o nullptr // socios = nullptr; // tam = 0; // // //se a origem da copia for um clube sem socios // if (ob.socios == nullptr || ob.tam == 0) // { // return *this; // } // //se a origem da copia for um clube com socios // socios = new Pessoa * [ob.tam]; // //copiar o tam // tam = ob.tam; // //copiar os ponteiros // for (unsigned int i = 0; i < tam; i++) // { // //nao se duplicam os cosios porque a relacao entre clube e socios e de agregacao // //o destrutor tambem nao os destroi // socios[i] = ob.socios[i]; // } // return *this; //} //v1 //string Clube::getAsString() const //{ // ostringstream oss; // oss << "\nTam: " << tam << endl; // for (unsigned int i = 0; i < tam; i++) { // if (socios[i] != nullptr) { // oss << socios[i]->getAsString() << endl; // } // } // // return oss.str(); //} //v2 vectores string Clube::getAsString()const { ostringstream oss; oss << "\nTam: " << socios.size() << endl; for (unsigned int i = 0; i < socios.size(); i++) { if (socios[i] != nullptr) { oss << socios[i]->getAsString() << endl; // tem de uusar -> } } return oss.str(); } int main() { //agregacao entre o clube e as pessoas //relação entre o clube e o array de ponteiros é de composição Pessoa a("aaa", 1, 111), b("bbb", 2, 222); Clube* clube1 = new Clube(50); clube1->setMembroDoclube(&a, 0); clube1->setMembroDoclube(&b, 1); cout << "Clube1: " << clube1->getAsString(); Pessoa c("ccc", 3, 333), d("ddd", 4, 444); Clube* clube2 = new Clube(10); clube2->setMembroDoclube(&a, 0); clube2->setMembroDoclube(&b, 1); clube2->setMembroDoclube(&c, 2); clube2->setMembroDoclube(&d, 3); cout << "Clube2: " << clube2->getAsString(); *clube1 = *clube2; //atribuir os apontados delete clube2; cout << "Clube2 depois: " << clube1->getAsString(); //copia do apontado Clube clube3(*clube1); cout << "Clube3 depois: " << clube3.getAsString(); cout << "\nfim do main" << endl; }
………..ficha5, exercicio7 (a17)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; //relação da agenda com contacto é de composição //qd a agenda for destruida os contactos tb são //contactos: objecto em memória dinamica //usar vectores, sem necessidade de indicar quantidades //matriz dinamics de ponteiros class Contacto { string nome; unsigned int tel; public: Contacto() = delete; Contacto(const string& s, unsigned int t); string getNome()const; void setNome(const string& n); unsigned int getTel()const; void setTel(unsigned int t); string getAsString()const; }; Contacto::Contacto(const string& s, unsigned t):nome(s), tel(t) { } string Contacto::getNome() const { return nome; } void Contacto::setNome(const string& n) { this->nome = n; } unsigned Contacto::getTel() const { return tel; } void Contacto::setTel(unsigned t) { this->tel = t; } string Contacto::getAsString() const { ostringstream oss; oss << "Contacto " << " Nome: " << nome << "\tTel: " << tel; return oss.str(); } class Agenda { string nome; vector<Contacto*> contactos; //vector de ponteiros //o objecto composto vai destruir a string e o vector de ponteiros //mas não os apontados, tem que haver um destrutor que faça a libertação dos apontados //pois não existe mais nada no programa que conheça os apontados (ver destrutor da Agenda) int pesquisa(const string& s)const; public: Agenda(const string& nome0); Agenda(const Agenda& ob); //construtor por copia Agenda& operator=(const Agenda& ob); //operador = ~Agenda(); string getNome()const; void setNome(const string& nome0); bool acrescentaContacto(const string& n, unsigned int i); bool removeContacto(const string& n); string getAsString()const; const Contacto * getContacto(const string& s); bool gravaContactosEmFicheiroDeTexto(); bool lerContactosDeFicheiroDeTexto(); }; int Agenda::pesquisa(const string& s) const { for(unsigned int i = 0 ; i < contactos.size(); i++) { if(contactos[i]->getNome() == s) { return i; } } return -1; } Agenda::Agenda(const string& nome0):nome(nome0) { } Agenda::Agenda(const Agenda& ob) { //existem dois membros da classe: nome e vector *this = ob; cout << "\n CCop " << ob.getAsString() << endl; } Agenda& Agenda::operator=(const Agenda& ob) //a.operator(b), a apontado por this ou *this, //b é recebido pela ref. ob { //testar a auto atribuição if(this == &ob) { return *this; } //destruir os objectos apontados pelo primeiro membro de atribuição for(Contacto * c: contactos) { delete c; } //esvaziar o vector contactos contactos.clear(); //copiar o segundo membro da atribuição for(Contacto * c : ob.contactos) { Contacto* dupplicado = new Contacto(*c); contactos.push_back(dupplicado); } return *this; } Agenda::~Agenda() { //obrigatório por ser relação de composição //e existe uma estrutura de vectores de ponteiros for(Contacto * c: contactos) { delete c; } } string Agenda::getNome() const { return nome; } void Agenda::setNome(const string& nome0) { this->nome = nome0; } bool Agenda::acrescentaContacto(const string& n, unsigned int t) { int qual = pesquisa(n); if(qual != -1) { return false; } contactos.push_back(new Contacto(n, t)); return true; } bool Agenda::removeContacto(const string& n) { int qual = pesquisa(n); if(qual == -1) { return false; } delete contactos[qual]; //nunca sera erase, e primeiro faz-se sempre o erase e depois o delete //relação da classe é de composição: 1º deçete, 2º depois erase. //se a relação for de agregação é só o erase //sendo composição: //o delete: liberta a memoria dinamica apontada //o erase: tira a posição do vector, tira o ponteiro, erase será do vector //se fizer o delete e nao fizer o erase, o ponteiro fica apontar para lixo, e o destrutor vai fazer duplo delete, erro de execução //erase e depois o delete: erase da pos zero, e depois o delete de contactos zero.. não faz sentido contactos.erase(contactos.begin() + qual); return true; } string Agenda::getAsString() const { ostringstream oss; oss << nome << endl; for(Contacto * p: contactos) { oss << endl << p->getAsString(); } return oss.str(); } const Contacto* Agenda::getContacto(const string& s) { int qual = pesquisa(s); if(qual == -1) { return nullptr; }else { return contactos[qual]; } } bool Agenda::gravaContactosEmFicheiroDeTexto() { ofstream dados("dados.txt"); if (!dados.is_open()) { return false; } for (unsigned int i = 0; i < contactos.size(); i++) { dados << contactos[i]->getTel() << " " << contactos[i]->getNome() << endl; } dados.close(); if (dados) { return true; } else return false; } bool Agenda::lerContactosDeFicheiroDeTexto() { ifstream dados("dados.txt"); string nome; unsigned int tel; string s; if (!dados.is_open()) { return false; } // ler dados de todas as pessoas // uma por linha while (!dados.eof()) { // ler string com os dados da pessoa getline(dados, s); istringstream iss(s); // ler dados da pessoa iss >> tel >> std::ws; getline(iss, nome); // se correu bem a leitura ... if (iss) { acrescentaContacto(nome, tel); } } dados.close(); return true; } int main() { Agenda* a1 = new Agenda("a minha agenda 2021"); a1->acrescentaContacto("pedro", 1234); a1->acrescentaContacto("maria", 5678); cout << "\nAgenda: " << a1->getAsString() << endl; a1->gravaContactosEmFicheiroDeTexto(); Agenda* a2 = new Agenda("a minha agenda 2022"); a2->lerContactosDeFicheiroDeTexto(); cout << "\nAgenda: " << a2->getAsString() << endl; Agenda* a3 = new Agenda("a minha agenda especial"); *a3 = *a2; delete a2; a3->removeContacto("maria"); cout << "\nAgenda: " << a3->getAsString() << endl; cout << "\nsair do main" << endl; return 1; }
operadores
………..ficha4, exercicio1 (a13, a14)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; //os operadores //uso de operados de atribuiçao em situações de composição envolvendo memória dinâmica //aumentar a clareza do código por exemplo a.multiplica(b) po a * b //operadores podem ser globais ou membros //p.e.: globaL operator++(a,b) //p.e.: membro a.operator++(b) /* +---------------------------------------- + -------------------------- - + | operador membro / não membro | +---------------------------------------- + -------------------------- - + | todos os operadores unários p.e: ++a, a++ | membro | | = () -> ->* | têm sempre que ser membro | | += -= /= *= &= |= %= >>= <<= | membro | | todos os restantes operadores binários, ==,<<... | globais | +---------------------------------------- + -------------------------- - + */ //o retorno de um operador pode ser um valor ou uma cópia de algo //se o objectivo é usar o resultado de um operador, então este deve ser uma referência para algo, não constante e que pode ser modificado class Fraccao { int numerador; int denominador; public: //explicit //se usado trancamos o construtor, fazendo com que tenhamos que usar implicitamente Fracca(a) , na main //explicit, indicar ao construtor que só trabalhar quando o mandarmos trabalhar Fraccao(); Fraccao(int num); Fraccao(int num, int denom); int getNumerador() const; void setNumerador(const int numerador); int getDenominador() const; void setDenominador(const int denominador); string getAsString() const; //b v3 Fraccao operator*(const Fraccao& f); //operador * membro //j) Fraccao & operator*=(const Fraccao& f); //operador *= membro, não pode ser void //m) membro, operador unário Fraccao & operator++(); //++a Fraccao operator++(int n); //a++, int n, serve para poderem funcionar as duas, mesmo que nao faça nada //n) passamos uma fraccao mas a f recebe apenas um inteiro //operador de conversao operator int()const { return denominador/numerador; }; }; Fraccao::Fraccao() { numerador = 0; denominador = 1; } Fraccao::Fraccao(int num) :numerador(num) { denominador = 1; } Fraccao::Fraccao(int num, int denom):numerador(num) { setDenominador(denom); } int Fraccao::getNumerador() const { return numerador; } void Fraccao::setNumerador(const int numerador) { this->numerador = numerador; } int Fraccao::getDenominador() const { return denominador; } void Fraccao::setDenominador(const int denominador) { if(denominador >0) { this->denominador = denominador; }else if(denominador ==0) { this->denominador = 1; }else { this->numerador = -this->numerador; this->denominador = -denominador; } } string Fraccao::getAsString() const { ostringstream oss; oss << " ( " << numerador << " / " << denominador << " ) " << endl; return oss.str(); } //b, v3 Fraccao Fraccao::operator*(const Fraccao& f) { cout << "operator*(const Fraccao& f)\n"; Fraccao prod(getNumerador() * f.getNumerador(), getDenominador() * f.getDenominador()); return prod; } Fraccao & Fraccao::operator*=(const Fraccao & f) { cout << "operator*=(const Fraccao & f)\n"; numerador *= f.getNumerador(); denominador *= f.getDenominador(); return *this; } Fraccao& Fraccao::operator++() { numerador += denominador; //a fracao aumenta uma unidade return *this; //retorna objecto depois de incremento } Fraccao Fraccao::operator++(int n) { Fraccao aux = *this; //grava para esta varavel auxiliar o objecto antes de incrementar numerador += denominador; //a fracção aumenta uma unidade return aux; //não pode ser por referencia, retorna-se a variavel local } //b, v1 Fraccao produto(const Fraccao& x, const Fraccao& y) //função global { Fraccao prod(x.getNumerador() * y.getNumerador(), x.getDenominador() * y.getDenominador()); return prod; } //b, v2 Fraccao operator*(const Fraccao& x, const Fraccao& y) //função global { cout << "operator*(const Fraccao& x, const Fraccao& y)\n"; Fraccao prod(x.getNumerador() * y.getNumerador(), x.getDenominador() * y.getDenominador()); return prod; } //g ostream & operator<<(ostream & saida, const Fraccao & ob) { saida << ob.getAsString(); return saida; } //extra istream& operator>>(istream& entrada, Fraccao& ob) { int num, denom; entrada >> num, denom; if(entrada) { ob.setNumerador(num); ob.setDenominador(num); } return entrada; } //n) void func(int n) { cout << n; // aparece 2 } //o) (a==b) bool operator==(const Fraccao& op1, const Fraccao& op2); bool operator==(const Fraccao& op1, const Fraccao& op2) { return (op1.getNumerador() * op2.getDenominador() == op1.getDenominador() * op2.getNumerador()); } int main() { //a) /*Fraccao a(1, 2); Fraccao b(3); const Fraccao c(3, 4); cout << a.getAsString(); cout << b.getAsString(); cout << c.getAsString();*/ //b) multiplicação de duas fraccoes //v1 //Fraccao a(1, 2); //Fraccao b(3); //const Fraccao c(3, 4); //Fraccao a1; //a1 = produto(b,c); //cout << a1.getAsString(); //v2 //a = b * c; // a = operator(b,c); global //cout << a.getAsString(); //v3 (estando as duas opções disponiveis, global ou membro, recai sobre o membro) //a = b * c;// a = b.operator*(c); membro //cout << a.getAsString(); //c) //Fraccao a(1, 2); //Fraccao b(3); //const Fraccao c(3, 4); //a = a * b * c; //composição das funções, (a*b)*c //operator*(operator*(a,b),c) //cout << a.getAsString(); //d e e) //Fraccao a(1, 2); //Fraccao b(3); //a = b * 4; //b.operator*(4) //cout << a.getAsString(); //d e e) v2 construtor explicit //a = b * Fraccao(4); //só assim funcionaria //d e e) v3 //Fraccao a(1, 2); //Fraccao b(3); //a = 4 * b; //funciona apenas o global, se for membro só se usado o explicit, Fraccao(4) * b; //cout << a.getAsString(); //g) v1 //Fraccao a(1, 2); //cout << a; //<<, operador binário, tem que ser operador global //cout está a invocar a nossa operação, cout é da classes ostream, não é da nossa classe, logo << tem que ser global //g e h) v2 //Fraccao a(1, 2); //Fraccao b(3); //const Fraccao c(3, 4); //cout << a << b << c; //por ser global e ostream ao inves de void funciona em cadeia //i) ao inves de copia usar o valor, ostream & operator<<(ostream saida, const Fraccao & ob) //não dá...dá erro //extra //Fraccao a(1, 2); //Fraccao b(3); //cin >> a; //cout << a.getAsString(); //j) a *= b //Fraccao a(1, 2); //Fraccao b(2,3); ////a *= b; ////operador global: operator*=(a,b) ////operador membro: a.operator*=(b) //recomendada ////*= te como retorno //cout << "antes->" << " a: " << a << " b: " << b << endl; //a *= b; //cout << "depois->" << " a: " << a << " b: " << b << endl; //Fraccao a2(1, 2); //Fraccao b2(2, 3); //Fraccao c2(3, 4); //cout << "antes->" << " a2: " << a2 << " b2: " << b2 << " c2: " << c2 << endl; //a2 *= b2 *= c2; //cout << "depois->" << " a2: " << a2 << " b2: " << b2 << " c2: " << c2 << endl; //Fraccao a3(1, 2); //Fraccao b3(2, 3); //Fraccao c3(3, 4); //cout << "antes->" << " a3: " << a3 << " b3: " << b3 << " c3: " << c3 << endl; //(a3 *= b3) *= c3; //cout << "depois->" << " a3: " << a3 << " b3: " << b3 << " c3: " << c3 << endl; //l) retornar referencia ou valor //Fraccao a(1, 2), b(2, 3), c(3, 4); //cout << "antes->" << " a: " << a << " b: " << b << " c: " << c << endl; //(a *= b) *= c; //cout << "depois->" << " a: " << a << " b: " << b << " c: " << c << endl; //// é suposto aparecer 6/24 //cout << a; //cout << "expressao ((a *= b) *= c)" << ((a *= b) *= c) << endl; //retornar por referência Fraccao & Fraccao::operator*=(const Fraccao & f) //retornar por valor (é uma copia) Fraccao Fraccao::operator*=(const Fraccao & f) //m) posfixado, a++ e prefixado ++a //++a, global, operator++(a) //++a, membro, a.operator++() //não recebe nenhum argumento //prefixado //int n = 2; //cout << "++n= " << ((++n)) << endl; //3, e é possivel fazer uma atribuição (++n)=55, vem ++n=55 //cout << " n= " << n << endl; //3 ////posfixado //int n2 = 2; //cout << "n2++= " << ((n2++)) << endl; //2 //cout << " n2= " << n2 << endl; //3 //Fraccao a(1, 2), b(2, 3), c(3, 4); //(++a) = c; //cout << ++a; //a.operator++() //sem argumento //cout << a; //Fraccao a2(1, 2), b2(2, 3), c2(3, 4); //cout << a2++; //a.operator++(0) //0 é um argumento //cout << a2; //n) //const Fraccao f(7,3); //func(f); // é passado automaticamente o valor 7/3 // // arredondado para baixo //o) if(a==b) //tem que existir o operador== que retorne um bool, e é global Fraccao a(1, 2), b(1, 2); if(a== b) { cout << "oi"; }else { cout << "noi"; } cout << "\nfim do main" << endl; return 0; }
………..ficha4, exercicio3 (a14)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; /* +---------------------------------------- + -------------------------- - + | operador | membro / não membro | +---------------------------------------- + -------------------------- - + | todos os operadores unários p.e: ++a, a++ | membro | | = () -> ->* | têm sempre que ser membro | | += -= /= *= &= |= %= >>= <<= | membro | | todos os restantes operadores binários, ==,<<... | globais | +--- */ class Automovel { const string matricula; //const para proibir atribuições string marca; string modelo; int ano; static int nAutomoveisCriados; //tem que ser inicializada fora da classe public: Automovel(const Automovel& ob); Automovel(string matricula, string marca, string modelo, int ano); ~Automovel(); string getMatricula()const; string getMarca()const; string getModelo()const; int getAno()const; static int getNAutomoveisCriados(); void setMarca(string marca); void setModelo(string modelo); void setAno(int ano); string getAsString()const; Automovel& operator=(const Automovel& ob); //retorna uma referencia para o primeiro membro da atribuição }; int Automovel::nAutomoveisCriados = 0; ostream& operator<<(ostream& saida, const Automovel& ob); Automovel::Automovel(const Automovel& ob):matricula(ob.matricula), marca(ob.marca), modelo(ob.modelo), ano(ob.ano) { cout << "\nConstrutor por copia"; ++nAutomoveisCriados; //tambem por copia, os construidos por cópia tb devem ser contados! } Automovel::Automovel(string matricula1, string marca1, string modelo1, int ano1): matricula(matricula1), marca(marca1), modelo(modelo1), ano(ano1) { cout << "\nConstrutor com parametros"; ++nAutomoveisCriados; } Automovel::~Automovel() { cout << "\nDestruindo " << getAsString(); } string Automovel::getMatricula()const { return matricula; } string Automovel::getMarca()const { return marca; } string Automovel::getModelo()const { return modelo; } int Automovel::getAno()const { return ano; } int Automovel::getNAutomoveisCriados() { return nAutomoveisCriados; } void Automovel::setMarca(string marca1) { marca = marca1; } void Automovel::setModelo(string modelo1) { modelo = modelo1; } void Automovel::setAno(int ano1) { ano = ano1; } string Automovel::getAsString()const { ostringstream oss; oss << "\nAutomovel: " << matricula << " " << marca << " " << modelo << " " << ano; return oss.str(); } Automovel& Automovel::operator=(const Automovel& ob) { // if (this == &ob) { return *this; } cout << "\nOperador atribuicao "; //a matricula nao se altera no objecto destino da atribuição this->marca = ob.marca; this->ano = ob.ano; this->modelo = ob.modelo; return *this; } ostream& operator<<(ostream& saida, const Automovel& ob) { saida << ob.getAsString() << endl; return saida; } void f(Automovel x) { cout << x << endl; } int main() { Automovel a1("11-11-AA", "OPEL", "OMEGA", 2010); Automovel a2("22-22-BB", "FIAT", "PUNTO", 2011); Automovel a3 = a1; //a3 copia de a1, construtor por copia a1 = a2; //operador atribuição, mas havendo membros constantes, tem que se fazer o operador operator= //a1.operator=(a2) f(a1); return 1; }
composição, agregação, vectores, ficheiros
………..ficha3, exercicio1 (a8)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> using namespace std; //"nao deve ser possivel construir objectos desta classe sem a indicacação.." não pode haver construtor por omissão //"sem desrespeitar o encapsulamento.." gets e sets //"as funções que permitem obter os dados devem poder ser chamadas sobre bjectos constantes" devem conter o const class Ponto { private: //encapsulamento int x; int y; public: //Ponto(int x0, int y0); //necessário introduzir coordenadas ~Ponto(); Ponto(int x0 = 0, int y0 = 0); //construtor por omissão, e) //extra /*Ponto() { x = rand() % 100; y = rand() % 100; cout << "\nponto " << getAsString(); }*/ int getX() const { return x; } void setX(const int x) { this->x = x; } int getY() const { return y; } void setY(const int y) { this->y = y; } double calculaDistancia(const Ponto & outro)const; string getAsString()const; }; Ponto::Ponto(int x0, int y0) { x = x0; y = y0; cout << "\nPonto(in x0, int y0) " << getAsString(); } Ponto::~Ponto() { cout << "\n~Ponto() " << getAsString(); } double Ponto::calculaDistancia(const Ponto& outro) const { //pitágoras, calculo da distância double dist = (x - outro.x) * (x - outro.x) + (y - outro.y) * (y - outro.y); return sqrt(dist); } string Ponto::getAsString() const { ostringstream oss; oss << " ( " << x << " , " << y << " ) " << endl; return oss.str(); } int main() { Ponto a(1,1); Ponto b(2,2); cout << a.getAsString(); //as funções mmebros recebem um ponbteiro para o objectos que as invocou, daí tem acesso a e b cout << a.calculaDistancia(b); //c) const Ponto c(5, 6); //agora só posso chamar funcções que sejam const cout << c.getAsString(); cout << "c.getY(); " << c.getY(); //d) Ponto tab_d[] = {Ponto(1,3), Ponto(2,4), Ponto(5,7)}; //e) Ponto(int x0 = 0, int y0 = 0); //construtor por omissão, e) Ponto tab_e[3]; cout << "\nfim do main" << endl; return 0; }
………..ficha3, exercicio3 (a8, a9)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> using namespace std; //composição de objectos class Ponto { int x; int y; public: //Ponto(); //construtor por omissao //Ponto(int x0, int y0); Ponto(int x0=0, int y0=0); //construtor por omissão Ponto(const Ponto& z); ~Ponto(); int getX() const { return x; } void setX(const int x) { this->x = x; } int getY() const { return y; } void setY(const int y) { this->y = y; } string getAsString() const; }; Ponto::Ponto(int x0, int y0) { x = x0; y = y0; cout << "\nponto(int x0, int y0) " << getAsString(); } Ponto::Ponto(const Ponto& z) { x = rand() % 100; y = rand() % 100; cout << "\nponto(const Ponto& z) " << getAsString(); } Ponto::~Ponto() { cout << "~Ponto()"; } string Ponto::getAsString() const { ostringstream oss; oss << " ponto x" << x << " y " << y << endl; return oss.str(); } class Rectangulo //objecto composto { Ponto canto; int largura; int altura; public: Rectangulo(int x, int y, int largura0, int altura0); Rectangulo(const Ponto& p0, int largura0, int altura0); ~Rectangulo(); Ponto getCanto() const { return canto; } int getX()const { return canto.getX(); } int getY()const { return canto.getY(); } int getLargura() const { return largura; } int getAltura() const { return altura; } bool setLargura(int larg0); bool setAltura(int alt0); void setCanto(const Ponto& c); string getAsString() const; int calculaArea()const; }; Rectangulo::Rectangulo(int x, int y, int largura0, int altura0):canto(x,y) //canto(x,y) membro objecto { if(!setLargura(largura0)) { largura = 1; } if (!setAltura(altura0)) { altura = 1; } cout << "\nrectangulo int x, int y.." << getAsString(); } Rectangulo::Rectangulo(const Ponto& p0, int largura0, int altura0):canto(p0) { cout << "\nrectangulo const Ponto& p0.." << getAsString(); } Rectangulo::~Rectangulo() { cout << "~Rectangulo()" << getAsString(); } bool Rectangulo::setLargura(int larg0) { if(larg0<0) { return false; } largura = larg0; return true; } bool Rectangulo::setAltura(int alt0) { if(alt0<0) { return false; } altura = alt0; return true; } void Rectangulo::setCanto(const Ponto& c) { canto = c; } string Rectangulo::getAsString() const { ostringstream oss; oss << " rectangulo " << canto.getAsString() << " larg: " << largura << " alt: " << altura << endl; return oss.str(); } int Rectangulo::calculaArea() const { return largura * altura; } int main() { //a) relação de composição //b) //Rectangulo a(1, 2, 4, 2); //cout << "\na:" << a.getAsString() << endl; //Rectangulo b(1, 2, 44, 22); //cout << "\nb:" << a.getAsString() << endl; //os cantos são iguais, mas estão localizados em sitios diferentes de memória.. //cada rectangulo tem a posso exclusiva do seu canto. mas são diferentes const Rectangulo c(1, 2, 4, 2); c.getLargura(); cout << "\nfim do main" << endl; return 0; }
………..ficha3, exercicio4 (a9, a10)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> using namespace std; class Ponto { int x; int y; public: //Ponto(); //construtor por omissao //Ponto(int x0, int y0); Ponto(int x0 = 0, int y0 = 0); //construtor por omissão Ponto(const Ponto& z); ~Ponto(); int getX() const { return x; } void setX(const int x) { this->x = x; } int getY() const { return y; } void setY(const int y) { this->y = y; } string getAsString() const; //calcular a distancia entre dois pontos double calculaDistancia(const Ponto& outro)const; bool isIgual(const Ponto& outro)const; }; Ponto::Ponto(int x0, int y0) { x = x0; y = y0; //cout << "\nponto(int x0, int y0) " << getAsString(); } Ponto::Ponto(const Ponto& z) { x = rand() % 100; y = rand() % 100; //cout << "\nponto(const Ponto& z) " << getAsString(); } Ponto::~Ponto() { cout << "~Ponto()"; } string Ponto::getAsString() const { ostringstream oss; oss << " ponto x " << x << " y " << y << endl; return oss.str(); } double Ponto::calculaDistancia(const Ponto& outro) const { //pitágoras, calculo da distância double dist = (x - outro.x) * (x - outro.x) + (y - outro.y) * (y - outro.y); return sqrt(dist); } bool Ponto::isIgual(const Ponto& outro) const { return x == outro.x && y == outro.y; } class Rectangulo //objecto composto { Ponto canto; int largura; int altura; public: Rectangulo(int x, int y, int largura0, int altura0); //Rectangulo(const Ponto& p0, int largura0, int altura0); //Rectangulo(const Rectangulo& r); //construtor por cópia ~Rectangulo(); Ponto getCanto() const { return canto; } int getX()const { return canto.getX(); } int getY()const { return canto.getY(); } int getLargura() const { return largura; } int getAltura() const { return altura; } bool setLargura(int larg0); bool setAltura(int alt0); void setCanto(const Ponto& c); string getAsString() const; int calculaArea()const; }; Rectangulo::Rectangulo(int x, int y, int largura0, int altura0) :canto(x, y) //canto(x,y) membro objecto { if (!setLargura(largura0)) { largura = 1; } if (!setAltura(altura0)) { altura = 1; } //cout << "\nrectangulo int x, int y.." << getAsString(); } //Rectangulo::Rectangulo(const Rectangulo& r) //{ // *this = r; // cout << "\nRectangulo(const Rectangulo& r) " << getAsString() << endl; //} //Rectangulo::Rectangulo(const Ponto& p0, int largura0, int altura0) :canto(p0) //{ // cout << "\nrectangulo const Ponto& p0.." << getAsString(); //} Rectangulo::~Rectangulo() { cout << "~Rectangulo()" << getAsString(); } bool Rectangulo::setLargura(int larg0) { if (larg0 < 0) { return false; } largura = larg0; return true; } bool Rectangulo::setAltura(int alt0) { if (alt0 < 0) { return false; } altura = alt0; return true; } void Rectangulo::setCanto(const Ponto& c) { canto = c; } string Rectangulo::getAsString() const { ostringstream oss; oss << " rectangulo " << canto.getAsString() << " larg: " << largura << " alt: " << altura << endl; return oss.str(); } int Rectangulo::calculaArea() const { return largura * altura; } class Desenho { string nome; vector<Rectangulo> rectangulos; //coleccão de rectangulos //ao ser destruido o vector são destruidos os seus elementos, //são destruidos os rectangulos //existe assim uma relação de composição //relação de posse exclusiva public: Desenho(string nome0); ~Desenho(); void acrescentar(int x, int y, int alt, int larg); //um rectangulo int calculaAreda()const; vector<Rectangulo> pesquisarRectMesmoCanto(const Ponto& p)const; //const, função de consulta, e retorna o conjunto dos rect que satifazem a condição int somaDasAreas()const; int calculaArea()const; string getAsString() const; bool eliminarUm(unsigned int qual); int eliminarRectAreaSup(int areaLimite); }; Desenho::Desenho(string nome0)//:nome(nome0) { //ou nome = nome0; //ou //se nada for indicado, ele vem construido na mesma sendo usada o construtor por omissão } Desenho::~Desenho() { cout << "\n~Desenho" << getAsString(); } void Desenho::acrescentar(int x, int y, int larg , int alt) { Rectangulo rect(x, y, larg, alt); rectangulos.push_back(rect); //é feito uma cópia do rect para o vector, pelo construtor por cópia //ou //rectangulos.push_back(Rectangulo(x, y, larg, alt)); } int Desenho::calculaAreda() const { return 1; } vector<Rectangulo> Desenho::pesquisarRectMesmoCanto(const Ponto& p) const { vector<Rectangulo> v; for(const auto & rect : rectangulos) { //ou if(rect.getCanto().isIgual(p)) { v.push_back(rect); } //ou, mas falha o encapsulamento //pois é a classe do ponto que sabe se dois pontos são iguais //pois quem deve ter a funcionalidade é a classe que tem a informação correspondente //assim não deve ser a classe desenho a faze-lo //if(rect.getCanto().getX() == p.getX() && rect.getCanto().getY() == p.getY()) //{ // v.push_back(rect); //} } return v; } int Desenho::somaDasAreas() const { int soma = 0; //ou for(vector<Rectangulo>::const_iterator it = rectangulos.begin(); it != rectangulos.end(); ++it) { soma += it->calculaArea(); } //ou for(auto it = rectangulos.begin(); it != rectangulos.end(); ++it) { soma += it->calculaArea(); } //ou for(unsigned int i= 0; i< rectangulos.size(); i++) { soma += rectangulos[i].calculaArea(); } //ou for(const auto & elem:rectangulos) { soma += elem.calculaArea(); } return soma; } int Desenho::calculaArea() const { int area = 0; return area; } string Desenho::getAsString() const { ostringstream oss; oss << " nome " << nome << " tem " << rectangulos.size() << " rectangulos \n "; //ou for(unsigned int i = 0; i< rectangulos.size(); i++){ oss << rectangulos[i].getAsString() << endl; } //ou //for( auto & rect : rectangulos) //{ // oss << rect.getAsString() << endl;; //} //ou iteradores //for(vector<Rectangulo>::const_iterator it =rectangulos.begin(); it != rectangulos.end(); ++it) //{ // oss << it->getAsString() << endl; //} return oss.str(); } bool Desenho::eliminarUm(unsigned qual) //qual: indice { { if (qual < 0 || qual >= rectangulos.size()) { return false; } rectangulos.erase(rectangulos.begin() + qual); //o vector auto-reorganiza-se //é um iterator: rectangulos.begin() + qual return true; } } int Desenho::eliminarRectAreaSup(int areaLimite) { //serve para coleções //https://www.cplusplus.com/reference/vector/vector/erase/ int nEliminar = 0; for(vector<Rectangulo>::iterator it = rectangulos.begin(); it != rectangulos.end();) { if(it->calculaArea() > areaLimite) { it = rectangulos.erase(it); ++nEliminar; }else { ++it; } } return nEliminar; //não era necessaria esta informação } int main() { Desenho a1("janela"); cout << "\na1"; a1.acrescentar(1, 2, 3, 4); a1.acrescentar(5, 6, 7, 8); a1.getAsString(); cout << "\nfim do main.." << endl; return 0; }
………..ficha3, exercicio7 (a11, a12)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> #include <fstream> using namespace std; //relação entre banco, conta, pessoa e arquivo de identificacao: //relação entre banco e conta: composição (banco cria a conta) //quando o banco for destruído as contas tambem o são //relação entre conta e pessoa: agregação //relação entre arquivo de identificacao e pessoa: composição (arquivo cria a pessoa) class Pessoa { //objecto dinamico, que não muda. string nome; long bi; long nif; void setBi(long bi); //alteração é privado public: Pessoa(const string& s, long b, long n); Pessoa() = delete; //não disponbilizamos construtor por omissão, fica proibido ~Pessoa(); string getNome() const; void setNome(const string& nome); long getBi() const; long getNif() const; void setNif(const long nif); string getAsString()const; string getDados()const; }; Pessoa::~Pessoa() { cout << "~Pessoa()" << endl; } string Pessoa::getNome() const { return nome; } void Pessoa::setNome(const string& nome) { this->nome = nome; } long Pessoa::getBi() const { return bi; } void Pessoa::setBi(const long bi) { this->bi = bi; } Pessoa::Pessoa(const string& s, long b, long n):nome(s), bi(b),nif(n) { } long Pessoa::getNif() const { return nif; } void Pessoa::setNif(const long nif) { this->nif = nif; } string Pessoa::getAsString() const { ostringstream oss; oss << "nome: " << nome << " bi: " << bi << " nif: " << nif << endl; return oss.str(); } string Pessoa::getDados() const { //ficheiro de dados, linha ostringstream oss; oss << bi << " " << nif << nome << endl; return oss.str(); } class ArquivoDeIdentificacao { //vector de ponteiros para pessoas //ou vector de pessoas/objectos (não ia funcionar bem, porque na conta existe um ponteiro para pessoa, e se for eliminado no arquivo o ponteiro fica como?) //o destrutor tem que destruir primeiro as pessoas, libertar a memória com delete string nome; vector<Pessoa* > pessoas; int pesquisa(long bi)const; public: ArquivoDeIdentificacao(string nome0); ArquivoDeIdentificacao(const ArquivoDeIdentificacao& ob) = delete; //proibir a construção por cópia ArquivoDeIdentificacao& operator=(const ArquivoDeIdentificacao& ob) = delete; //proibir o operador atribuição, proibir a atribuição //esta dupla proibição tem a ver com evitar erros de execuação ~ArquivoDeIdentificacao(); string getNome() const; void setNome(const string& nome); bool acrescentaPessoa(string n, long b, long nif); //relação de posse o arquivo. string getDados()const; //para lidar com ficheiros const Pessoa * getPessoa(long bi); string getAstring()const; bool gravarPessoasEmFicheiroDeTexto(const ArquivoDeIdentificacao &arquivo); bool lerPessoasDeFicheiroDeTexto(ArquivoDeIdentificacao &arquivo); }; int ArquivoDeIdentificacao::pesquisa(long bi) const { //porque existe uma coleção for(unsigned int i = 0; i< pessoas.size(); i++) { if(pessoas[i]->getBi()==bi) { return i; } } return -1; } ArquivoDeIdentificacao::ArquivoDeIdentificacao(string nome0):nome(nome0) { } ArquivoDeIdentificacao::~ArquivoDeIdentificacao() { //o arquivo tem uma relação de composição: cria e destoi a pessoa for(Pessoa * p: pessoas) { cout << p->getAsString(); delete p; } //ou //for (int i = 0; i < pessoas.size(); i++) // delete pessoas[i]; } string ArquivoDeIdentificacao::getNome() const { return nome; } void ArquivoDeIdentificacao::setNome(const string& nome) { this->nome = nome; } bool ArquivoDeIdentificacao::acrescentaPessoa(string n, long b, long nif) { int qual = pesquisa(b); if(qual != -1) { return false; //já existe uma pessoa com este bi } pessoas.push_back(new Pessoa(n, b, nif)); //o arquivo tem uma relação de composição: cria e destoi a pessoa //new Pessoa(n, b, nif) //criar a pessoa em memória dinamica, e retorna o endereço //pessoas.push_back //acrescenta mais um elemento com o endereço da nova pessoa return true; } string ArquivoDeIdentificacao::getDados() const { //para lidar com ficheiros ostringstream oss; for(Pessoa * p: pessoas) { oss << p->getDados() << endl; } return oss.str(); } const Pessoa* ArquivoDeIdentificacao::getPessoa(long bi) //primeiro const: para nao alterar o ponteiro, damos acesso apenas aos dados para onde aponta (read only) { int qual = pesquisa(bi); if(qual == -1) { return nullptr; }else { return pessoas[qual]; } } string ArquivoDeIdentificacao::getAstring() const { ostringstream oss; for(Pessoa * p : pessoas) { oss << p->getAsString(); } return oss.str(); } bool ArquivoDeIdentificacao::gravarPessoasEmFicheiroDeTexto(const ArquivoDeIdentificacao & arquivo) { ofstream dados("dados.txt"); if (!dados.is_open()) { return false; } dados << arquivo.getDados(); dados.close(); if (dados) { return true; } else return false; } bool ArquivoDeIdentificacao::lerPessoasDeFicheiroDeTexto(ArquivoDeIdentificacao& arquivo) { ifstream dados("dados.txt"); string nome; long bi; long nif; string s; if (!dados.is_open()) { return false; } // ler dados de todas as pessoas // uma por linha while (!dados.eof()) { // ler strig com os dados da pessoa getline(dados, s); istringstream iss(s); // ler dados da pessoa iss >> bi >> nif; getline(iss, nome); // se correu bem a leitura ... if (iss) { acrescentaPessoa(nome, bi, nif); } } dados.close(); return true; } class Conta { //ponteiro para pessoa const Pessoa* titular; //const, para não alterar int saldo; public: Conta(const Pessoa* const titular); //posso usar um ponteiro para const ou sem const //ou //Conta(Pessoa* const titular); //pode alterar o apontado desta forma ~Conta(); const Pessoa* getTitular()const; //não altera, damos o conhecimento int getSaldo()const; bool depositar(int quantia); bool levantar(int quantida); long getBi()const; string getAsString()const; }; Conta::Conta(const Pessoa* const titular):titular(titular), saldo(0) { } Conta::~Conta() { cout << "~Conta()" << endl; } const Pessoa* Conta::getTitular() const { return titular; } int Conta::getSaldo() const { return saldo; } bool Conta::depositar(int quantia) { if (quantia <= 0) return false; saldo += quantia; return true; } bool Conta::levantar(int quantia) { if (quantia <= 0 || quantia > saldo) return false; saldo -= quantia; return true; } long Conta::getBi() const { return titular->getBi(); } string Conta::getAsString() const { ostringstream oss; oss << "\ntitular " << titular->getAsString() << "\nsaldo " << saldo << endl; return oss.str(); } class Banco { string nome; //vector de objectos contas vector <Conta> contas; //a destruição deste obejcto vai destruir os membros de outras classes: vector e nome int pesquisar(long bi)const; public: Banco(string n); //~Banco(); //não é preciso, porque as contas não sobrevivem ao fim do banco string getNome() const; void setNome(const string& nome); bool acrescentar(const Pessoa* const pessoa); bool eliminar(long bi); int somaDosSaldos()const; bool depositar(long bi, int quantia); bool levantar(long bi, int quantia); void eliminarTodasAsContas(long bi); string getAsString()const; }; int Banco::pesquisar(long bi) const { for(unsigned int i=0; i< contas.size(); i++) { if(bi == contas[i].getBi()) { return i; } } return -1; } Banco::Banco(string n):nome(n) //vai funcionar o construtor por omissão, e fica vazio, sem lixo/elementos { } //Banco::~Banco() //{ // cout << "\n~Banco" << endl; //} string Banco::getNome() const { return nome; } void Banco::setNome(const string& nome) { this->nome = nome; } bool Banco::acrescentar(const Pessoa* const pessoa) { if (pessoa == nullptr){ return false; } contas.push_back(Conta(pessoa)); //criar contas, é o banco que o faz return true; } bool Banco::eliminar(long bi) { int qual = pesquisar(bi); if(qual == -1) { return false; } contas.erase(contas.begin() + qual); return true; } int Banco::somaDosSaldos() const { int soma = 0; for(const Conta &c : contas) { soma += c.getSaldo(); } return soma; } bool Banco::depositar(long bi, int quantia) { int qual = pesquisar(bi); if(qual == -1) { return false; } return contas[qual].depositar(quantia); } bool Banco::levantar(long bi, int quantia) { int qual = pesquisar(bi); if(qual == -1) { return false; } return contas[qual].levantar(quantia); } void Banco::eliminarTodasAsContas(long bi) //vector { { for (auto it = contas.begin(); it != contas.end();) { if (it->getBi() == bi) { it = contas.erase(it); } else { ++it; } } } } string Banco::getAsString() const { ostringstream oss; oss << "\n== Banco : " << nome << "\t n.contas " << contas.size() << endl; for (unsigned int i = 0; i < contas.size(); i++) { oss << contas[i].getAsString(); } return oss.str(); } int main() { //Pessoa p("coimbra", 123, 321); //cout << p.getAsString(); //Pessoa* ap=nullptr; //ou //Pessoa* const ap = nullptr; //preciso que Conta(const Pessoa* const titular); //ou //const Pessoa* const ap=nullptr; //preciso que Conta(const Pessoa* const titular); //Conta c1(ap); /*Pessoa p1("AAA", 123, 321); Pessoa p2("BBB", 928, 890); Banco b("o banco b"); b.acrescentar(&p1); b.acrescentar(&p2); cout << "Banco \n" << b.getAsString() << endl; b.depositar(123, 40); b.depositar(928, 240); cout << "Bancos depositos\n" << b.getAsString() << endl;*/ //ArquivoDeIdentificacao arquivo("coimbra"); //ArquivoDeIdentificacao arquivo2("porto"); //ArquivoDeIdentificacao arquivo3(arquivo2); //dá erro, proibida a cópia de arquivo2, construção por cópia //arquivo1 = arquivo2; //dá erro, poribida a atribuição , atribuição //arquivo.acrescentaPessoa("pedro", 123, 456); //arquivo.acrescentaPessoa("ines", 888, 999); // //cout << "arquivo de identificacao\n" << arquivo.getAstring(); //ArquivoDeIdentificacao arquivo("coimbra final"); //arquivo.acrescentaPessoa("pedro", 123, 456); //arquivo.acrescentaPessoa("ines", 888, 999); //cout << "arquivo de identificacao\n" << arquivo.getAstring(); // //Banco banco("filial coimbra"); //banco.acrescentar(arquivo.getPessoa(123)); //banco.acrescentar(arquivo.getPessoa(888)); //banco.acrescentar(arquivo.getPessoa(88)); //banco.acrescentar(arquivo.getPessoa(123)); //cout << "banco" << banco.getAsString() << endl; //operação grava ficheiro //ArquivoDeIdentificacao arquivo("coimbra"); //arquivo.acrescentaPessoa("pedro", 123, 456); //arquivo.acrescentaPessoa("ines", 888, 999); //cout << "arquivo de identificacao\n" << arquivo.getAstring(); //Banco banco("filial coimbra"); //banco.acrescentar(arquivo.getPessoa(123)); //banco.acrescentar(arquivo.getPessoa(888)); //banco.acrescentar(arquivo.getPessoa(88)); //banco.acrescentar(arquivo.getPessoa(123)); //cout << "banco" << banco.getAsString() << endl; //arquivo.gravarPessoasEmFicheiroDeTexto(arquivo); //operação ler ficheiro //ArquivoDeIdentificacao arquivo2("porto final"); //arquivo2.lerPessoasDeFicheiroDeTexto(arquivo2); // //cout << "arquivo de identificacao\n" << arquivo2.getAstring(); //Banco banco("filial coimbra"); //banco.acrescentar(arquivo2.getPessoa(123)); //banco.acrescentar(arquivo2.getPessoa(888)); //banco.acrescentar(arquivo2.getPessoa(88)); //banco.acrescentar(arquivo2.getPessoa(123)); //cout << "banco" << banco.getAsString() << endl; cout << "\nfim do main" << endl; return 0; }
Encapsulamento, classes, construtores, destrutores
………..ficha2, exercicio3e4 (a4,a5)
#include <string> #include <iostream> #include <sstream> #include <vector> using namespace std; class Caderno { private: //membros variáveis string marca; string cor; int numFolhas; string tamanho; public: Caderno(const string& marca, const string& cor, const int num_folhas, const string& tamanho) //const não consigo alterar o argumento que vem da main, assim fica mais eficiente/seguro, //e tambem assim posso fazer uso de uma const, usando por exemplo "marca" //o int pode ter const ou não e referencia, e porque é uma coisa pequena : marca(marca), cor(cor), numFolhas(num_folhas), tamanho(tamanho) { } //membros funções //funções get //os consts sigificam que não podem alterar nada, é uma função de consulta string getMarca() const { return marca; } string getCor() const { return cor; } int getNumfolhas() const { return numFolhas; } string getTamanho() const { return tamanho; } //funções set void setMarca(const string & marca) { this->marca = marca; } void setCor(const string & cor) { this->cor = cor; } void setNumfolhas(int num_folhas) { if(num_folhas>0){ numFolhas = num_folhas; }else { numFolhas = 0; } } void setTamanho(const string& tamanho) { this->tamanho = tamanho; } string getAsString()const; }; string Caderno::getAsString()const { ostringstream oss; oss << "\nmarca: " << marca << "\ncor: " << cor << "\nnum folhas: " << numFolhas << "n\ntamanho: " << tamanho; return oss.str(); } int main() { const Caderno Cad1("bic","branca",2,"a4"); //const não posso alterar cout << Cad1.getAsString(); cout << "\nfim do main" << endl; return 0; }
………..ficha2, exercicio5 (a5)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> using namespace std; class Automovel { private: string matricula; string combustivel; string marca; string modelo; int ano; static int conta; //não surge nos objectos, surge fora dos objectos, membro estático //e vai precisar de ser inicializado, no inicio e surge int Automovel::conta = 0; //validar a matricula do automovel bool validaMatricula(const string & s) const ; public: Automovel(const string& matricula, const string& combustivel, const string& marca, const string& modelo, const int ano) : combustivel(combustivel), marca(marca), modelo(modelo) //o ano nao surge aqui fica com lixo { setMatricula(matricula); //construida por omissão setAno(ano); //construida por omissão ++conta; } string getMatricula() const { return matricula; } string getCombustivel() const { return combustivel; } string getMarca() const { return marca; } string getModelo() const { return modelo; } int getAno() const { return ano; } static int getConta(); void setMatricula(const string & matricula) { if(validaMatricula(matricula)){ this->matricula = matricula; } //this->matricula = "99-AA-99"; } void setCombustivel(const string& combustivel) { this->combustivel = combustivel; } void setMarca(const string& marca) { this->marca = marca; } void setModelo(const string& modelo) { this->modelo = modelo; } void setAno(const int ano) { if (ano > 2000 && ano < 2021) { this->ano = ano; } else { this->ano = 0; } } string getAsString()const; }; int Automovel::conta = 0; //vai ser criado logo no inicio do main int Automovel::getConta() { return conta; } string Automovel::getAsString()const { ostringstream oss; oss << "\nmatricula: " << matricula << "\ncombustivel: " << combustivel << "\nmarca: " << marca << "n\\nmodelo: " << modelo << "\nano: " << ano; return oss.str(); } bool Automovel::validaMatricula(const string & s) const { //v1 //if (s.length() != 8) //{ // return false; //} //return true; //v2 expressões regulares string patern = "((\d{2}-\d{2}-[A-Z]{2}) | (\d{2}-[A-Z]{2}-\d{2}) | (\[A-Z]{2}-\d{2}-\d{2}))"; //string patern = R"((\d{2}-\d{2}-[A-Z]{2}) | (\d{2}-[A-Z]{2}-\d{2}) | (\[A-Z]{2}-\d{2}-\d{2}))"; //string patern = R"(([A-Z]{2}-\d{2}-(\d{2}|[A-Z]{2}))|(\d{2}-(\d{2}-[A-Z]{2}|[A-Z]{2}-\d{2})))"; regex reg(patern); if(regex_match(s, reg)) { return true; } return false; } int main() { cout << "\nconta: " << Automovel::getConta() << endl; Automovel A1("11-22-CD", "gasolina", "opel", "corsa", 2020); cout << A1.getAsString(); cout << "\nconta: " << Automovel::getConta() << endl; cout << "\nfim da main"; return 0; }
………..ficha2, exercicio7 (a6,a7)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> using namespace std; class MSG { private: char letra; int numero; static int contador; //MSG(const MSG& ob); //i) public: MSG(char letra = 'X'); //por valor por omissão, um constutor por omissão //MSG(char letra); //alternativa que tb funcionar a aliena e) ~MSG(); //construtor por cópia MSG(const MSG& ob); char getLetra() const { return letra; } void setLetra(const char letra) { this->letra = letra; } int getNumero() const { return numero; } void setNumero(const int numero) { this->numero = contador; } string getAsString() const; }; int MSG::contador = 0; MSG::MSG(char letra) { this->letra = letra; this->numero = ++contador; cout << "\ncriado: " << getAsString() << endl; } MSG::~MSG() { cout << "\nterminado: "<< getAsString() << endl; } //construtor por cópia c) MSG::MSG(const MSG& ob) { //anular o construtor por cópia por defeito que é fornecido pelo compilador letra = ob.letra; numero = -ob.numero; //para fazer uma coisa diferente (numero simétrico) cout << "\nCriado por copia " << getAsString(); } string MSG::getAsString() const { ostringstream oss; oss << "\nletra " << getLetra() << " numero " << getNumero() << endl; return oss.str(); } void teste_g() { MSG aux('y'); } void teste_h(MSG z) { //h) //a passagem de um objecto como parametro por valor (funcionou o construtor por cópia) //(z é copia de b, e funciona o construtor por cópia) } void teste_l(MSG & z) //passar o objecto por referencia { //l) //uma referencia não tem o seu lugar proprpio, vai ser apenas outro nome para b, //z coincide em memoria com b, não surge outro objecto, é apenas outro nome //constutor e destrutor são os mesmos } MSG teste_m() { MSG m; //é criado localemnte um objecto m return m; //retornar o m por valor } MSG & teste_n() //retorno por referencia, para uma variavel que já existe { MSG m; //é criado localemnte um objecto m return m; //retornar o m com estatuto de referencia ( não existe uma cópia ) } int main() { //objectos não dinamicos, que vai ser destruido no fim do main //MSG m1('a'); //MSG m2; //para fazer uso do default //objectos dinâmicos //MSG* p = new MSG('D'); //cout << "\n vou construir D"; //delete p; //destruo o objecto apontado por p, destruido expressamente //cout << "\n vou destruir D"; //MSG & m3 = m2; //alinea b) //m3 é uma referência que se vai "colar" a m2, m3 é um outro nome para m2 //não vai ser criado mais nenhum objecto //c) //MSG m4 = m2; //não existe nenhum construtor por cópia implementado //mas funionou um //todas as classe que não tenham um construtor por cópia, o compilador fornece um //a nossa construção do construtor por cópia //MSG m4 = m2; //criado por cópia //d) atribuição //MSG m4 = m2; //criado por cópia //m1 = m2; //atribuição //e) matriz (array) de objectos //MSG mat1[] = {'M', 'N'}; //f) matriz (array) de objectos //MSG mat2[2]; //criado se e só existir um constutor por omissão //ou //MSG m1('a'); //MSG m2; //para fazer uso do default //MSG mat[] = { m1, m2 }; //ou //MSG mat[4] = { MSG('A') , MSG('B') }; //ou //MSG mat[4] = { MSG() , MSG() }; //g) /*cout << "\n alinea g)" << endl; teste_g(); cout << "\n alinea g)" << endl; teste_g();*/ //h) //MSG b; //cout << "\n alinea h)" << endl; //teste_h(b); //cout << "\n alinea h)" << endl; //teste_h(MSG('c')); //a passagem de um objecto como parametro por valor (funcionou o construtor por cópia) //(z é copia de b, e funciona o construtor por cópia) //i) e j) //em privado o construtor po cópia MSG(const MSG & ob); //mas sendo privado a cópia não vai funcionar, teste_h(b); //k) //objecto é criado com lixo, e é bloqueada a construção por cópia do compilador //l) /*MSG b; teste_l(b);*/ //m) //MSG b = teste_m(); //o valor m chegou aqui //, mas m perde a validade no primeiro }, morre aqui, é terminado //vai ser feita uma copia de m e é este que vai representar a informação a seguir ao = do teste_m() //quem actua é o construtor por cópia, o return é uma cópia //n) retorno por referência MSG b = teste_n(); // cout << "\nfim do main" << endl; return 0; }
………..ficha2, exercicio9 (a7, a8)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> using namespace std; class Televisao { vector<string> canais; //estes endereços podem mudar de sítio //acrescentar elementos ao canais.push_back("rtp1"); //string s = "sic"; //canais.push_back(s); //percorrer: //for(int i = 0; i< canais.size(); i++){ // cout << canais[i]; //} //esvaziar tudo: //canais.clear(); //size a zero //alternativa será uso de vectores de ponteiros para objectos dinâmicos (dinâmicos vão estar no seu lugar) bool ligada; int volume; int canal; //indice do vector de canais +1 string nomeCanal; //o conjunto de canais definido na construção não se altera a partir do exterior da classe void setCanais(string canais0[], unsigned int n); public: Televisao(string canais0[], int n); bool isLigada()const; //is por ser booleana void ligar(); void desligar(); void aumentaVolume(); void diminuiVolume(); void mudaCanal(int c); int getCanal() const { return canal; } //para experimentar Televisao(initializer_list<string> canais0); //lista de constantes, separadas por virgulas e chavetas void setCanais(initializer_list<string> canais0); void setCanais2(vector<string> canais0); string getAsString()const; }; void Televisao::setCanais(string canais0[], unsigned int n) { //validação if(n< 0) { n = 0; } //se já tiver previamente preecnhido canais.clear(); for(unsigned int i = 0; i < n; ++i) { canais.push_back(canais0[i]); //copiar os elementos para o vector de strings } } Televisao::Televisao(string canais0[], int n) { setCanais(canais0, n); ligada = false; volume = 0; canal = 1; cout << "\nNormal" << endl; } bool Televisao::isLigada() const { return ligada; } void Televisao::ligar() { ligada = true; } void Televisao::desligar() { ligada = false; } void Televisao::aumentaVolume() { if (ligada != true) return; if (volume <= 10) volume++; } void Televisao::diminuiVolume() { if (ligada != true) return; if (volume >= 10) volume--; } void Televisao::mudaCanal(int c) { if (ligada != true) return; unsigned cc = c; if (cc > 0 && cc <= canais.size()) canal = c; } Televisao::Televisao(initializer_list<string> canais0) { setCanais(canais0); ligada = false; volume = 0; canal = 1; cout << "\nInitializer" << endl; } void Televisao::setCanais(initializer_list<string> canais0) { canais.clear(); //ou //for (const auto& c : canais) //{ // canais.push_back(c); //} //ou //for (initializer_list<string>::iterator it = canais0.begin(); it != canais0.end(); ++it){ // canais.push_back(*it); //} //ou for(auto it = canais0.begin(); it != canais0.end(); ++it) { canais.push_back(*it); } } void Televisao::setCanais2(vector<string> canais0) { canais.clear(); for (auto it = canais0.begin(); it != canais0.end(); ++it) { canais.push_back(*it); } } string Televisao::getAsString() const { ostringstream oss; //for(unsigned int i = 0; i < canais.size() ; ++i) //{ // //oss << canais[i] << " "; // oss << canais.at(i) << " "; //} //for (const string& c : canais) //c é um outro nome para cada um dos elementos da coleção // oss << c << " "; //for (string c : canais) //c é uma copia dos elementos da coleção // oss << c << " "; //for (const auto & c : canais) // oss << c << " "; //iterador aponta para os elementos do objecto e desta forma conseguimos percorrer //for (vector<string>::const_iterator it = canais.begin(); it != canais.end(); ++it) // oss << *it << " "; for( auto it = canais.begin() ; it != canais.end(); ++it) { oss << *it << " "; } if(!ligada) { oss << "desligada\n"; return oss.str(); } if(canais.empty()) { oss << "sem canais "; return oss.str(); } oss << "\nCanal" << canais[canal-1] << " " << canal << "\nvolume: " << volume << endl; return oss.str(); } int main() { //exemplos dos inicializer lists Televisao tv = { "um","dois","tres","quatro" }; Televisao tv2{ "um", "dois" }; Televisao tv3({ "um","dois" }); //outro exemplo e se for construtor e fizer uso dos () tv.setCanais2({ "um","dois","tres" }); //uniform inicialization //usar estes construtor Televisao(string canais0[], int n); string canais[] = { "um","dois" }; Televisao tv4(canais, 2); Televisao tv5{ canais, 2 }; Televisao tv6 = { canais, 2 }; cout << "\nfim do main" << endl; return 0; }
………..ficha2, exercicio5 (a10, a11)
#include <string> #include <iostream> #include <sstream> #include <vector> #include <regex> #include <initializer_list> using namespace std; class Prego { int x, y; public: Prego(int a, int b); ~Prego(); void mudaDeSitio(int a, int b); string getAsString() const; int getX()const { return x; } int getY()const { return y; } }; Prego::Prego(int a, int b):x(a), y(b) { //x = a; y = b; cout << "construindo prego em " << x << "," << y << "\n"; } Prego::~Prego() { cout << "~Prego em " << x << "," << y << "\n"; } void Prego::mudaDeSitio(int a, int b) { x = a; y = b; } string Prego::getAsString() const { ostringstream oss; oss << "Prego: ( " << x << " , " << y << " ) "; return oss.str(); } class Aviso { string texto; //Prego prego; //relação de composição. //Prego * prego; //usamos um ponteiro, já não é uma posse exclusiva //aviso é destruído, o prego fica lá.. //com ponteiro pode haver posse excluiva ou não //Prego * const prego; //não é possivel mudar o endereço do prego //recebe um valor e não pode mudar, usa-se o const const Prego * const prego; //O aviso nao te direito a fazer alterações ao Prego... const Prego //a relação entre aviso e prego é de agregação (tem o conhecimento) //porque se o aviso for destruido o prego não é //o prego pode ser partilhado por mais de um aviso //o prego já existe antes do aviso //o aviso tem o conhecimento do prego public: Aviso(const string & t, const Prego * const p); ~Aviso(); int getX() const; int getY() const; string getAsString() const; Aviso(const Aviso& ob); //construtor por cópia }; Aviso::Aviso(const string& t, const Prego * const p):texto(t), prego(p) { //prego = p //nao é possivel usar porque o Prego é um membro constante de Aviso (ou até podia ser uma referencia) //logo tem que ser usada a outra opção //os membros const e os membros referencias é obrigatório inicializar na lista de inicialização do construtor cout << " construido Aviso " << texto << endl; } Aviso::~Aviso() { cout << " ~Aviso() " << texto << endl; } int Aviso::getX() const { return prego->getX(); } int Aviso::getY() const { return prego->getY(); } string Aviso::getAsString() const { ostringstream oss; oss << "Aviso: " << texto << " " << prego->getAsString(); return oss.str(); } Aviso::Aviso(const Aviso& ob):prego(ob.prego) { texto = ob.texto+"Copia"; cout << "CCopia de aviso " << texto << endl; } int main() { Prego p(1, 2); Aviso a("quadro 1", &p); Aviso b("quadro 2", &p); cout << " A " << a.getAsString() << endl; cout << " B " << b.getAsString() << endl; p.mudaDeSitio(99, 99); cout << " A " << a.getAsString() << endl; cout << " B " << b.getAsString() << endl; //a=b; //erro //a atribuição entre dois objectos é feita membro a membro //o texto consegue atribuir, mas o prego pk tem const não consegue //assim a atribuição default dá erro, podemos é usar uma atribuição que funcione //usando o operador atribuição Aviso c = a; cout << " C " << c.getAsString() << endl; vector<Aviso> avisos; cout << "-pushback-\n"; avisos.push_back(a); Aviso* pa = &avisos[0]; //ponteiro para o primeiro elemento do vector cout << pa->getAsString() << endl; avisos.push_back(b); Aviso* pb = &avisos[0]; cout << pb->getAsString() << endl; if(pa==pb) { cout << " == \n"; }else { cout << " != \n"; } cout << avisos[0].getAsString() << endl; //avisos.erase(avisos.begin() + 1); //dá erro, pk os objectos do vector têm um membro constante //a atribuição dá erro, e o erase precisa de atribuições internamente cout << "\nfim do main" << endl; return 0; }
POO, introdução
A programação é orientada não pela decomposição de tarefas mas pela identificação das entidades que constituem o problema e como são modelizadas e manipuladas
os mecanismos:
Encapsulamento;
Herança;
Encapsulamento:
integridade da informação numa única estrutura a informação, e as funções que trabalham essa informação
acesso à informação é muito controlada
Herança:
coisas em comuns entre conceitos
(funcionários, funcionário administrativo, gestor, …)
sub-classes, surgem as estruturas que derivadas ou herdar do “pai”
Polimorfismo:
poli, várias, morfismos, formas
a mesma instrução pode ser usada em diferentes ações com significado análogo
as Classes, têm:
Estruturas
Classes
Construtores
Destrutor
Funções membros inline
Membros variáveis estáticos
Funções membros estáticas
Funções membros constantes
Funções friend
Construtores – lista de inicialização
Construtores – membros constantes não estáticos
Construtores – membro referência
Membros variáveis inicializados na declaração ( C++11)
Construtores que chamam construtores ( C++11)
Membros default e delete ( C++11)
passagens por referencia vs passagem por valor
nas classes deve haver relações explicitas entre a estrutura e as funções surgem as funções membros
. é a notação para aceder aos membros das classes
os membros podem ser:
privados (por defeito se fosse uma struct era ao contrário), em privado, as variáveis públicas, as funções membro por norma (funções: set, imprimir..)
funções:
get, servem para obter valores, para comparar (retornam copias dos valores)
criamos as funções/métodos usando :: (por exemplo: Estrutura::get..)
:: são funções que pertencem à classe, não são globais
funções inline:
o código é expandido no local da chamada
funções pequenas
chamadas muitas vezes
p.e. detetar se um numero é par
declara-se a função com a expressão inline
inline int par(int numero){
…
}
funções overloaded:
têm o mesmo nome no mesmo contexto
distinguem-se pelo número de argumentos
p.e.
data(“11/11/11”);
data(11,11,11);
void data (char * s)
void data(int n1, int n2, int n3)
função destrutor:
funciona automaticamente, quando um objeto é destruído ou perde a validade;
podemos precisar numa classe;
usada quando o objeto é destruído e precisamos de executar uma ação de seguida;
libertar por exemplo um espaço de memória dinâmica
o destrutor não tem parâmetros como o construtores
Os objetos locais não estáticos são destruídos quando a execução do programa sai do bloco onde foram declarados.
Os objetos globais são destruídos quando o programa termina.
Os objetos dinâmicos são destruídos com o operador delete.
e os objetos são destruídos por ordem inversa da construção
funções com argumentos por omissão:
A lista de parâmetros com os valores por omissão deve estar apenas na declaração da função cuidado com os erros de ambiguidade (onde existe mais do que uma hipótese)
membros e funções membros estáticos:
têm que ser criados fora dos construtores
são partilhados por todos os objetos da classe
existe apenas uma única versão
está sempre fora dos objetos
p.e.
um predio e um elevador
o prédio tem andares que são objectos
o elevador é o membro estático, que todos usam
e quem usar o elevador pode deixar lá um aviso/atualização
(é uma especie de global na classe)
também existem funções estáticas
a chamada de uma função estática deve ser sempre com ::, mesmo se o objeto existir
funções
no fim, não altera os membros dos objetos
Funções membros constantes, const :
não podem alterar os membros variáveis do objecto
são funções de consulta
poem-se na declaração e na implementação
se um objecto é const, então todas as funções têm que ser const, mesmo que essa função não altere nada
Apenas uma função membro declarada como constante pode ser invocada por objectos constantes.
p.e.:
const int * p;
ou
int const * p;
const antes do *, o ponteiro é um ponteiro que só serve para consultar o apontado, não altera o apontado, mas pode ser apontando para outro local em memória
const depois do *, o ponteiro é constante, tem um endereço e não pode ter outro, não pode apontar para outro sitio
int * const p = &i;
ponteiro constante para constante, const antes e depois do *
const int * const p = &i;
ou
int const * const p = &i;
aponta para uma localização de memória e não se pode alterar essa localização de memória
e também não pode alterar o apontado
parâmetros do tipo ponteiro para constante:
apenas se passa o endereço
garantia de não se alterar a variável
p.e.:
void g(const Elemento * p) //eficiência e segurança
int main(){
Elemento val;
g(&val);
}
ou
void f(const Elemento & arg) //referencia por constante: eficiência e segurança – c++
Funções friend
é uma função que, não sendo membro da classe, tem acesso aos seus membros privados
cuidado que isto é uma quebra do encapsulamento e da integridade dos dados
só deve ser usada com uma justificação forte!
vectores:
notação:
vector v1 = {…};
ou
vector v1(..);
limpar vectores:
v1.clear()
percorrer vectores:
for (const string &c : canais0) {canais.push_back(c);}
ou usando interadores
for(auto it = canais0.begin; it!= canais0.end(); ++it){
canais.push_back(*it);
}
o iterador trata-se como se fosse um ponteiro
initializer_list:
é uma coleção de vectores constantes de um tipo especificado
p.e.
class Televisao{
public:
Televisao(initializer_list<string>canais0);
void setCanais(initializer_list<string>canais0);
}
para criar um objecto do tipo Televisao:
Televisao tv ={“um”, “dois”}; //tv é um objecto Televisao
Usar initializer_list como argumento:
tv.setCanais({“um”, “dois”});
Criar um objeto anónimo
tv = {“um”, “dois”};
inicialização de vectores com construtores:
class Televisao{
vector canais;
public:
Televisao(string canais0[], int n);
}
em que p.e.:
string canais[] = {“um”, “dois”};
Televisao tv = {canais, 2};
ou
Televisao tv {canais, 2};
dedução de tipo: auto
o compilador determina automaticamente o tipo de uma variável
p.e.:
vector::const_rever_interator pesquisa();
p.e.:
int tabela[10];
for(auto & elemento : tabela)
instrução
o uso da referência &, podemos alterar os elementos da coleção (mais eficiente, não faz cópias)
o não uso da referência &, é uma copia do coleção
ou em alternativa para obter eficiência e segurança
for(const auto & elemento : tabela) //não alteramos nada
o nulltpr, é uma constante que representa um ponteiro nulo
0 e NULL são do tipo int em funções overloaded
as referências:
é um outro nome para uma variável, é como que um ponteiro constante que automaticamente conduz ao objeto apontado
p.e.:
void f(int &n);
int main(){
int i =0;
f(i);
cout << x << endl; //100
}
void f(int &n){
n=100;
}
uma referencia como retorno:
int & f();
int x = 0;//global
int main(){
f() = 100;
cout << x << endl; // 100
}
int & f(){
return x;
}
………..construtores, membros constantes não estáticos:
se numa classe existem membros const é obrigatório inicializar no construtor
class Test
{
const int t;
public:
Test(int t0) : t(t0)
………..construtores, membro referência:
class Test2
{
int & t;
public:
Test2(int &t0) : t(t0)
{
};
int getT() const
{
return t;
}
};
int main()
{
int x = 10;
Test2 t2(x);
cout << t2.getT() << endl;
}
membro default:
construtor = default, damos à classe um comportamento default
respeita a inicialização na zona privada
assim o default disponibiliza
membro delete:
vai tornar indisponível a implementação de construtor por omissão, construtor por cópia, operador atribuição, e destrutor
assim o delete impede/proíbe que exista:
construtor por omissão, construtor por cópia, operador atribuição, e destrutor
………..Variáveis em memória dinâmica:
operador new: requisitar memória, e que invoca o construtor
operador delete: para libertar a memória dinâmica
p.e.:
pVar = new tipo;
delete pVar;
p.e.:
Produto* p = new Produto();
cout << p->getAsString() << endl;
delete p;
posso fazer uma reserva com:
int *v2 = new int() //() garantem que vem 0
delete v2
objectos em memória dinâmica:
uso do new, reserva memória para uma variável do tipo classe, e desencadeia o construtor
uso do delete, activa a execução do destrutor
p.e.:
class Ponto {
int x; // abcissa
int y; // ordenada
public:
Ponto() { cout << “Construindo \n”; x = 0; y = 0; }
~Ponto() { cout << “Destruindo \n”; }
void imprime() { cout << “x=” << x << ” y=” << y << endl; } }; int main(void) { Ponto * p = new Ponto; p->imprime();
delete p;
}
existe uma alternativa a:
Ponto * p = new Ponto;
que pode ser com argumentos
Ponto * p = new Ponto{2,2};
criando por exemplo:
criando por exemplo:
Ponto(int x0, int y0) { cout << “Construindo \n”; x = x0; y = y0; }
………..criar um array dinâmico de variváveis de um tipo:
criar:
Ponto * p = new Ponto[4];
libertar:
delete [] pVar
os operadores:
o ponteiro this:
implicitamente nas funções normal membro, não estáticia, a palavra this representa o objecto que invocou a função
………..operadores overloaded:
p.e. existem dois ou mais objectos de uma determinada classe
podemos querer aplicar os operadores que se usam para os tipos primitivos, como é o caso de:
+
–
*
/
%
=
+=
e vão ter as mesmas regras de prioridade e associatividade
expandidmos assim para operandos das classes
usamos assim o operator
p.e. operator+
………..operadores binários:
p.e.: se o operador estiver relacionado com a atribuição, =, em que os dois operandos não têm o mesmo estatuto é um operador membro, para dar resposta a z+=x ou (z+=x)=y, e também para dar resposta a operações de cadeia
public:
Complexo & operator+=( const Complexo & x); //operador membro
//global, mas membro da classe,
Complexo & Complexo::operator+=( const Complexo & x){
a += x.a;
b += x.b;
return *this; //estatuto de referência
}
//a opção agora é global, porque têm o mesmo estatutuo, são consultados apenas
Complexo operator+(const Complexo & x, const Complexo & y){
// const Complexo & -> para funcionar em cadeia, p.e., para responder a z = x + y +z; (exame)
return Complexo( x.getA() + y.getA() , x.getB() + y.getB() );
}
//e global
ostream & operator<<( ostream & saida, const Complexo & x){
// recebe cout: ostream & saida -> é alterado logo usa-se o &, com a informação do objecto
// ostream & operator -> para funcionar em cadeia, p.e., para responder a cout << x << y; (exame)
return saida << “(“<<x.getA() << ” , ” << x.getB() << “)”;
}
para fazer:
z = x + y;
cout << z << endl;
………..os operadores unários:
++
—
que podem ser:
pre-fixados (recomendado que sejam membros), p.e.: ++a, que vai ser interpretada como aa.operator++()
ou
pos-fixados (recomendado que sejam membros), p.e.: a++, que vai ser interpretada como aa.operator++(0)
p.e.:
pre-fixado
public:
Complexo & operator++();
e global
Compelxo & Complexo::operator++(){
a +=1;
retun *this;
}
pos-fixado
public:
Complexo operator++(int);
e global
Compelxo Complexo::operator++(int){
Complexo anterior(a,b); //guardamos o valor velho num outro objecto
a +=1;
retun anterior; //retornamos o velho
}
//neste caso não retrno uma referência
………..operador []
usado em classes, classes com coleções, como arrays ou vectores
operator[]()
x[y] é equivalente x.operator[](y)
serve:
para acedermos a um elemento da coleção, e quebra com o encapsulamento
p.e.:
class Tabela {
static const int DIM = 10;
static double erro;
double a[DIM];
int n; // numero de elementos utilizados
public:
// …
double & operator[](int i);
};
double Tabela::erro = 0;
usando:
double & Tabela::operator[](int i) {
if (i >= 0 && i < DIM) return a[i];
else {
cout << “\nIndice ” << i << ” fora dos limites”;
return erro;
}
}
int main() {
Tabela x;
x[0] = 20; // equivalente a x.operator[](0) = 20;
cout << “\n x[0]=” << x[0] << endl;
x[40] = 55;
}
………..operador chamada de função
usada em situações bidimensionais, por exemplo obter um numero de uma matriz bidimensional
usa dois parentesis curvos
operação tem que ser membro
chama.se usando operator()()
p.e.:
class Matriz {
static const int DIM = 10;
static double erro;
double a[DIM][DIM];
int lin; // numero de linhas utilizadas
int col; // numero de colunas utilizadas
public:
//….
double & operator()(int i, int j);
};
double Matriz::erro = 0;
double & Matriz::operator( )(int i, int j) {
if (i >= 0 && i < DIM && j >= 0 && j < DIM)
return a[i][j]; //com estatuto de referência
else {
cout << “\nIndices i=”
<< i << ” e j=” << j << ” fora dos limites”;
return erro;
}
}
int main() {
Matriz x;
x(1, 0) = 44; // x.operator()( 1,0) = 44; (escrever 44 lá..)
cout << “\n x(1,0)=” << x(1, 0);
x(-1, 100) = 12.1;
}
POO, introdução III
………..herança
se for composição um classe tem outras classes
se for derivação, sendo um caso particular com caracteristicas adicionais
uma classe derivada: tem membros herdados, e membros adicionais
class Terrestre : public Veiculo { // classe derivada: Terrestre, classe base: Veiculo
int velocidade;
public:
// …
};
a classe derivada acede aos gets e sets das funções que herdou
class Terrestre : public Veiculo
por se usar public:
herança publica
os membros publicos da classe base, são também da classe derivada
e por essa via será também um membro publico da classe derivada
class Terrestre : private Veiculo
ou
class Terrestre : Veiculo
por se usar private:
os membros publicos da herança, são privados de terrestre
o uso de protected:
é um nivel de acesso
são visiveis na classe
nas classes derivadas são visiveis
no código exterior, que não a classe ou classe derivadas não são acessiveis
Forma de derivação
private
membros que na classe base são:
private
protected
public
na classe derivada são:
inacessíveis
private
private
Forma de derivação
protected
membros que na classe base são:
private
protected
public
na classe derivada são:
inacessíveis
private
private
Forma de derivação
public (normal)
membros que na classe base são:
private
protected
public
na classe derivada são:
inacessíveis
protected
public
p.e.:
class Veiculo {
int peso;
protected:
int comprimento;
public:
Veiculo( int p = 0, int c=0): peso (p), comprimento(c){}
int getPeso()const{ return peso; }
int getComprimento()const{ return comprimento; }
void setPeso( int p){ peso = p;}
void setComprimento( int c){ comprimento = c;}
};
class Terrestre : public Veiculo {
int velocidade;
public:
Terrestre(int p=0,int c=0,int vel=0):Veiculo(p,c) {
velocidade = vel;
}
int getVelocidade()const{ return velocidade; }
void setVelocidade( int vel){ velocidade = vel;}
void imprime(ostream & saida )const{
saida << “Peso: ” << getPeso() << endl;
saida << “Compr: ” << comprimento << endl;
saida << “Veloc: ” << velocidade << endl;
}
};
int main(){
Veiculo v(700);
cout << “Peso: ” << v.getPeso() << endl;
//cout <<“Compr:” << v.comprimento << endl; ERRO , é protected em veiculo
Terrestre t(600, 4, 90);
cout << “Peso: ” << t.getPeso() << endl;
//cout <<“Compr:” << t.comprimento << endl; ERRO, é protected em veiculo
cout<<“Veloc: “<< t.getVelocidade() << endl;
t.imprime( cout);
}
Quando um objecto de uma classe derivada é destruído, são chamados os destrutores por ordem inversa da ordem de derivação: primeiro o destrutor da classe derivada, depois o destrutor da classe base
situação:
temos duas classes iguais, temos que usar o nome da herança Herança::funcao
situação: uma classe derivada e composta
p.e.:
class Base {
int i;
public:
Base(int ii = 0) : i(ii) { cout << “Base(int ii)\n”; }
~Base() { cout << “~Base()\n”; }
void imprime(ostream& saida)const {
saida << “Base: i=” << i << endl;
}
};
class Membro {
int k;
public:
Membro(int kk = 0) : k(kk) { cout << “Membro(int kk)\n”; }
~Membro() { cout << “~Membro()\n”; }
void imprime(ostream& saida)const {
saida << “Membro: k=” << k << endl;
}
};
class Derivada : public Base {
int j;
Membro m;
public:
Derivada(int jj) : Base(jj), j(jj), m(jj) {
cout << “Derivada(int jj)\n”;
}
~Derivada() { cout << “~Derivada()\n”; }
void imprime(ostream& saida)const {
saida << “Derivada: j=” << j << endl;
Base::imprime(saida);
m.imprime(saida);
saida << endl;
}
};
int main() {
Derivada d(2);
cout << “\nValores em d:\n”;
d.imprime(cout);
// chamada ao destrutor para d
}
situação: a classe derivada nao tem construtor nem destrutor
p.e.:
class Base {
int i;
public:
Base(int ii = 0) : i(ii) { cout << “Base(int ii)\n”; }
~Base() { cout << “~Base()\n”; }
void imprime(ostream& saida)const {
saida << “Base: i=” << i << endl;
}
};
class Membro {
int k;
public:
Membro(int kk = 0) : k(kk) { cout << “Membro(int kk)\n”; }
~Membro() { cout << “~Membro()\n”; }
void imprime(ostream& saida)const {
saida << “Membro: k=” << k << endl;
}
};
class Derivada : public Base {
Membro m;
public:
void imprime(ostream& saida)const {
saida << “Derivada: \n”;
Base::imprime(saida);
m.imprime(saida);
saida << endl;
}
};
int main() {
Derivada d;
cout << “\nValores em d:\n”;
d.imprime(cout);
// destruição de d
}
análise:
surge o construtor gerado automaticamente
é chamado o construtor da base e do membro
e na destruição invoca o membro e depois a base
também é respeitado o funcionamento da base e derivada, pela ordem com que são construídos e destruídos
………..funções com tratamento especial relativamente à herança
situações:
Construtores:
um construtor de uma classe derivada chama explicitamente ou implicitamente o construtor da classe base correspondente.
Construtor por cópia:
no construtor por cópia de uma classe derivada é preciso chamar explicitamente o construtor por cópia da classe base.
Destrutores:
um destrutor de uma classe derivada chama o destrutor da classe base correspondente.
Operador atribuição:
a atribuição entre dois objectos de uma classe pode não ser suficiente para fazer a atribuição entre dois objectos de uma classe derivada. O operador atribuição não é herdado normalmente como pode ver-se no exemplo seguinte.
p.e.:
class Base {
int i;
public:
Base(int ii) : i(ii) {}
Base(const Base & b) : i(b.i) {
cout << “Base(const Base &)\n”;
}
Base & operator=(const Base & b){
i = b.i;
cout << “Base::operator=()\n”;
return *this;
}
void imprime( ostream & saida)const{
saida << “Base: i=” << i << endl;
}
};
class Membro {
int k;
public:
Membro(int kk) : k(kk) {}
Membro(const Membro& m) : k(m.k) {
cout << “Membro(const Membro&)\n”;
}
Membro & operator=(const Membro & m){
k = m.k;
cout << “Membro::operator=()\n”;
return *this;
}
void imprime( ostream & saida)const{
saida << “Membro: k=” << k << endl;
}
};
class Derivada : public Base {
int j;
Membro m;
public:
Derivada(int jj) : Base(jj), j(jj), m(jj) {}
void imprime( ostream & saida)const{
saida << “Derivada: j=” << j << endl;
Base::imprime(saida);
m.imprime(saida);
}
};
int main() {
Derivada d(2);
cout << “\nConstrução por copia : ” << endl;
Derivada d2 = d; // construção por copia
cout << “\nValores em d2:\n”;
d2.imprime(cout);
Derivada d3(3);
cout << “\nAtribuicao: ” << endl;
d3 = d; // atribuição
cout << “\nValores em d3:\n”;
d3.imprime(cout);
}
………..herança, composição e construtor por cópia
outra versão da classe Derivada, com construtor por cópia e operador atribuição explícitos.
se nada dissermos na derivada, o construtor que é chamado é o da base por omissão e ainda assim temos que ter em atenção de que Base pode preencher com coisas por default
assim é necessário que se explicitem nestas chamadas a lista de inicialização do construtor por cópia, seriam chamados em seus lugares os correspondentes pois caso não sejam, construtores por defeito. O sub-objecto adquirido por herança e os membros objectos seriam inicializados pelos construtores por defeito em vez de receberem cópias dos correspondentes sub-objectos do objecto da classe Derivada considerado origem da cópia.
p.e.:
class Base {
int i;
public:
Base(int ii) : i(ii) {}
Base(const Base& b) : i(b.i) {
cout << “Base(const Base &)\n”;
}
Base& operator=(const Base& b) {
i = b.i;
cout << “Base::operator=()\n”;
return *this;
}
void imprime(ostream& saida)const {
saida << “Base: i=” << i << endl;
}
};
class Membro {
int k;
public:
Membro(int kk) : k(kk) {}
Membro(const Membro& m) : k(m.k) {
cout << “Membro(const Membro&)\n”;
}
Membro& operator=(const Membro& m) {
k = m.k;
cout << “Membro::operator=()\n”;
return *this;
}
void imprime(ostream& saida)const {
saida << “Membro: k=” << k << endl;
}
};
class Derivada : public Base {
int j;
Membro m;
public:
Derivada(int jj) : Base(jj), j(jj), m(jj) {}
Derivada(const Derivada& d) : Base(d), m(d.m), j(d.j) {
cout << “Derivada(const Derivada&)\n”;
}
Derivada& operator=(const Derivada& d) {
Base::operator =(d);
m = d.m;
j = d.j;
cout << “Derivada::operator=()\n”;
return *this;
}
void imprime(ostream& saida)const {
saida << “Derivada: j=” << j << endl;
Base::imprime(saida);
m.imprime(saida);
}
};
………..polimorfismo, clases derivadas
uma espécie de, ou um tipo de, é uma herança
o upcasting, é feito do tipo ascendente, e não é preciso fazer nada, nenhum cast explicito.
p.e.:
class Veiculo {
/* … */
};
class Terrestre : public Veiculo {
/* … */
};
class Automovel : public Terrestre {
/* … */
};
assim é possivel que:
conversão de um ponteiro para Terrestre num ponteiro para Veiculo;
conversão de um ponteiro para Automovel num ponteiro para Veiculo;
conversão de uma referência para Terrestre numa referência para Veiculo;
conversão de uma referência para Automovel numa referência para Veiculo.
isto é:
class Veiculo {
/* … */
};
class Terrestre : public Veiculo {
/* … */
};
int main(){
Veiculo *p;
Veiculo v;
Terrestre t;
p=&v;
p=&t; // upcasting
Veiculo & r = t; // upcasting
}
p=&t; // upcasting
um ponteiro para uma classe base pode receber um endereço de um objecto de uma classe derivada ou derivada da derivada..
pode assim ser feito por inteiro ou pode ser feito por referencia:
Veiculo & r = t; // upcasting
o upcasting é sempre verdade
o downcasting nem sempre é verdade
uso na base da expressão virtual na base e nas suas funções (desta forma será sempre usada a referência para o objeto que é referido)
e na derivada faz-se uso nas funções de override
Um ponteiro ou uma referência para uma classe base podem apontar para, ou referir, um objecto dessa classe ou de uma classe derivada directa ou indiretamente e é invocada:
é executada a versão da função da classe correspondente ao ponteiro ou referência, se a função não for virtual;
é executada a versão da função correspondente à classe a que pertence o objecto, se a função for virtual;
p.e.:
class Veiculo { //classe base
int peso;
public:
Veiculo(int p = 0) : peso(p) {}
virtual void imprime(ostream& saida) const { //virtual
saida << “\nPeso: ” << peso << endl;
}
};
class Terrestre : public Veiculo { //classe derivada
int velocidade;
public:
Terrestre(int p = 0, int vel = 0) : Veiculo(p), velocidade(vel) {}
void imprime(ostream& saida)const override { //lidar com o virtual
Veiculo::imprime(saida);
saida << “Veloc: ” << velocidade << endl;
}
};
class Automovel :public Terrestre { //classe derivada de derivada
int cilindrada;
public:
Automovel(int p = 0, int vel = 0, int cil = 0) : Terrestre(p, vel), cilindrada(cil) {}
void imprime(ostream& saida)const override { //lidar com o virtual
Terrestre::imprime(saida);
saida << “Cilind: ” << cilindrada << endl;
}
};
class Aereo :public Veiculo {
int altura_voo;
public:
Aereo(int p = 0, int a = 0) : Veiculo(p), altura_voo(a) {}
//não tem imprime recebe por herança da base
};
ostream& operator<<(ostream& saida, const Veiculo& x) {
x.imprime(saida);
return saida;
}
int main() {
Veiculo* a[] = { // array de ponteiros para a classe base, Veiculo
new Veiculo(700), // inicialização
new Terrestre(800,80), // inicialização: upcasting
new Automovel(900,90,1400), // inicialização: upcasting
new Aereo(2000,50) // inicialização: upcasting
};
int n = sizeof(a) / sizeof(Veiculo*); //contar elementos
for (int i = 0; i < n; i++) //perocrrer o array
a[i]->imprime(cout); // ou cout << *a[i];
for (int i = 0; i < n; i++)
delete a[i];
}
………..polimorfismo, classes abstractas e funções virtuais
uma classe é abstracta se a classe tem pelo menos um função virtual pura
uma classe abstracta não pode criar objetos
uma classe abstracta pode ter ponteiros e referências
faz sentido existir um prototipo da função que representa apenas um interface comum das classes desta hierarquia
p.e.:
virtual string meio() = 0; // é uma função virtual pura e fica uma classe abstracta
e esta classe pode conter ou pode não conter uma definição da função.
vais er necessário definir na classe derivada todas as funções virtuais puras adquiridas por herança
a classe derivada continua abstracta, se não for concretizada pelo menos uma função virtual pura adquirida por herança e por esse motivo não pode haver objectos desta classe
p.e.:
class Veiculo { //classe abstracta
int peso;
public:
Veiculo(int p = 0) : peso(p) {}
virtual void imprime(ostream & saida) const = 0; //função abstracta, ou virtuais puras
virtual string meioOndeSeDesloca()const = 0; //função abstracta, ou virtuais puras
int getPeso()const { return peso; }
};
class Terrestre : public Veiculo {
int velocidade;
public:
Terrestre(int p = 0, int vel = 0) :
Veiculo(p), velocidade(vel) {}
void imprime(ostream & saida)const override {
saida << “\nPeso: ” << getPeso() << endl << “Veloc: ” << velocidade << endl;
} //faz o overide da função abstracta imprime, da base: herdou duas, concretizou uma
// não se define ainda a função meioOndeSeDesloca(), e por este motivo esta é uma classe abstracta
};
class Automovel :public Terrestre { //esta classe tem as duas funções concretas: concretizou meioOndeSeDesloca e redefiniu a concreta imprime
int cilindrada;
public:
Automovel(int p = 0, int vel = 0, int cil = 0) : Terrestre(p, vel), cilindrada(cil) {}
virtual void imprime(ostream & saida)const override {
Terrestre::imprime(saida);
saida << “Cilind: ” << cilindrada << endl;
} //redefine a função imprime
string meioOndeSeDesloca()const override {
return string(” desloca-se em estrada \n”);
}//herdou uma função asbtracta e concretizou
};
class Aereo :public Veiculo { //concretizou as duas funções, logo é uma classe concreta
int alturaVoo;
public:
Aereo(int p = 0, int a = 0) : Veiculo(p), alturaVoo(a) {}
void imprime(ostream & saida)const override {
saida << “\nPeso: ” << getPeso() << endl << “Altura de voo: ” << alturaVoo << endl;
}
string meioOndeSeDesloca()const override {
return string(” desloca-se por via aerea \n”);
}
};
ostream & operator<<(ostream & saida, const Veiculo & x){
x.imprime(saida);
return saida;
}
int main() {
Veiculo* a[] = { //pode-se criar um array de ponteiros de classes abstractas
//new Veiculo(700), // ERRO, é uma classe abstracta, e não se podem criar objectos de classes abstractas
//new Terrestre(800,80), // ERRO, é uma classe abstracta, e não se podem criar objectos de classes abstractas
new Automovel(900,90,1400), //é uma classe concreta
new Aereo(2000,50) //é uma classe concreta
};
int n = sizeof(a) / sizeof(Veiculo*);
for (int i = 0; i < n; i++)
cout << *a[i] << a[i]->meioOndeSeDesloca();
delete a[0];
delete a[1];
}
………..polimorfismo, destrutores virtuais
O destrutor de uma classe base pode não ser suficiente para “destruir” um objecto de uma classe derivada
o uso delete, vai invocar o destrutor:
se o destrutor for virtual na classe do ponteiro, vai ser usado o destrutor de classe apontado
se o destrutor não for virtual na classe do ponteiro, vai ser usado o destrutor da classe ponteiro, que não é o destrutor da classe do objeto que está a ser destruído
na herança, o destrutor da classe derivada é executado mas depois chama o destrutor da base
p.e.:
class Veiculo {
int peso;
public:
Veiculo(int p = 0) : peso(p) {}
virtual ~Veiculo() { cout << “Destruindo Veiculo\n”; } //é virtual
};
class Aereo :public Veiculo {
int altura_voo;
string * historico;
int n;
public:
Aereo(int p = 0, int a = 0) : Veiculo(p), altura_voo(a), historico(nullptr), n(0) {}
virtual ~Aereo()override { //foi feito o override
delete[] historico;
cout << “Destruindo Aereo\n”;
}
};
int main() {
Veiculo* p = new Aereo(2000, 50);
// …
delete p; // funciona o destrutor de Aereo
// porque o destrutor foi declarado como virtual na classe Veiculo
}
………..polimorfismo, Colecção polimórfica e composição, duplicação polimórfica
existem agora elementos diferentes na colecção
(ver exemplo do aquário)
queremos fazer uma copia (duplicar), se fossem todos iguais eram com o new, mas neste caso temos elementos diferentes.
a solução:
duplicação polimorfica
na classe base é definida uma função virtual que duplica o objeto
mas não podem haver objectos da classe base, porque ela é a abstracta
nas classes derivadas é concretizada a função duplica, e esta função vai fazer o return new Derivada(*this) <-“é o construtor por cópia gerado automaticamente” a funcionar
e na classe base, surge na função de atribuição a duplicação dos objetos, recorrendo a Peixe* p = orig.peixes[i]->duplica();
p.e.:
class Peixe {
// …
};
class TetraPeixe : public Peixe { //classe derivada
// …
};
class NeonPeixe : public Peixe { //classe derivada
// …
};
class Aquario {
// o aquario contém uma colecção de peixes que podem ser de diversos tipos
vector<Peixe*> peixes; //vector de ponteiros para peixes, e pode ser cada um de sua classe, é um vector polimorfico
// …
public:
Aquario() = default;
// Os peixes são criados e destruídos no aquario
// A relação entre o aquário e os peixes é de composição
Aquario(const Aquario& orig); // construtor por copia
Aquario& operator=(const Aquario& orig); // operador atribuicao
virtual ~Aquario(); // destrutor
// …
// funcao abstracta, nao se podem criar objectos desta classe
virtual void alimentar(int quantidade, Aquario* aquario) = 0;
// duplicação polimórfica dos peixes
// como a classe é abstracta, esta função também tem que ser abstracta porque
// não se podem criar objectos da classse Peixe
virtual Peixe* duplica()const = 0; //duplicação polimórfica
};
Aquario::~Aquario() {
for (Peixe* p : peixes) {
delete p;
}
}
class TetraPeixe : public Peixe {
// …
public:
// …
// cria uma cópia de si mesmo em memória dinâmica
Peixe* duplica()const override;
};
Peixe* TetraPeixe::duplica()const { //garante a duplicação
return new TetraPeixe(*this);
}
class NeonPeixe : public Peixe {
// …
public:
// …
// cria uma cópia de si mesmo em memória dinâmica
Peixe* duplica()const override;
};
Peixe* NeonPeixe::duplica()const { //garante a duplicação
return new NeonPeixe(*this);
}
Aquario& Aquario::operator=(const Aquario& orig) { //operador atribuição
// prevencao da auto-atribuicao
if (this == &orig) {
return *this;
}
// libertar memoria dinamica do primeiro membro da atribuição
for (int i = 0; i < peixes.size(); i++) {
delete peixes[i];
}
//esvaziar o vector
peixes.clear();
// copiar a informacao de orig, duplicando os objectos dinamicos do tipo Peixe
for (int i = 0; i < orig.peixes.size(); i++) {
Peixe* p = orig.peixes[i]->duplica();
peixes.push_back(p);
}
// …
return *this;
}
Aquario::Aquario(const Aquario& orig) { //construtor por cópia
//funcionou o construtor por omissao do vector de ponteiros
//o vector está vazio
// não há ponteiros com valores indefinidos
*this = orig;
}
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
}
POO, introdução IV
………..enumerações
enum class Letras {a,b,c,d,e};
para se usar ou atribuir
Letras x = Letras::A;
x = Letras::C;
if (x == Letras::C) cout << ” x == C \n”;
………..conversões explicitas
para Conversões implícitas
double d = 0.0;
int x = static_cast<int>(d); // Mais explícita
………..Excepções
O tratamento de exceções geralmente é usado quando uma parte de um programa detecta um problema (excepção) que não pode resolver e …
ou precisa de uma maneira de sinalizar que algo aconteceu e que não pode continuar;
ou depois de sinalizar a condição excepcional, pára o processamento.
assim:
a instrução throw que a parte de um programa que detecta a excepção utiliza para lançar a excepção – sinalizar a situação que não consegue resolver
blocos try e cláusulas catch – as excepções lançadas do código executado dentro de um bloco try geralmente são tratadas por uma das cláusulas catch.
em que:
quando é lançado um throw o bloco de código fica interrompido (será lançada e não é tratada)
mas pode ser usado algo similar ao seguinte:
try {
// …
// Código que pode gerar excepções
// …
} catch( Erro1 e1 ) {
// Trata excepções do tipo Erro1
} catch( Erro2 e2 ) {
// Trata excepções do tipo Erro2
} catch( Erro3 e3 ) {
// Etc…
} catch( ErroN eN ){
// Trata excepções do tipo ErroN
}
// Continua a execução “normal”…
se existir pelo menos uma cláusula catch que possa corresponder ao tipo da excepção lançada
se não existir nenhuma cláusula catch que possa corresponder ao tipo da excepção lançada, a situação de erro continua
quando temos um try:
no throw nome(), nome() siginfica para procurar um catch com esse nome()
o catch (…), significa qualquer outro erro que não seja dos contemplados
p.e.:
class Excep {
public:
string what()const { return string(“Excep\n”); }
};
class A {
public:
A() { cout << “Construtor de A\n”; }
~A() { cout << “Destrutor de A\n”; }
};
void f() {
A obF;
cout << “–>f: antes de lancar a excepcao\n”;
throw Excep();
cout << “–>f: depois de lancar a excepcao\n”;
}
int main() {
try {
cout << “main:antes de chamar f()\n”;
f();
cout << “main:depois de chamar f()\n”;
}
catch (Excep & ex) { //é criado menos um objecto, porque a passagem é por referência
cout << ex.what();
}
cout << “main:depois de tratar a excepcao\n”;
}
Para relançar uma excepção capturada numa cláusula catch, utiliza-se a instrução throw sem argumentos. As cláusulas catch seguintes (correspondendo ao mesmo bloco try) são ultrapassadas apesar deste relançamento, sendo a excepção remetida para o próximo contexto envolvente.
Mesmo com o relançamento de uma excepção, nunca é executada mais do que uma cláusula catch de entre as que correspondem a um mesmo bloco try.
p.e.:
throw;
p.e.:
class Avaria {
string mensagem;
public:
Avaria(const char* msg = “”) :mensagem(msg) {}
string what() const { return mensagem; }
};
class AvariaLigeira : public Avaria {
public:
AvariaLigeira() : Avaria(“Avaria ligeira:\n”) {}
};
class AvariaGrave : public Avaria {
public:
AvariaGrave() : Avaria(“Avaria grave:\n”) {}
};
void assistenciaEmViagem(int avaria) {
try {
cout << “\n…assistencia em viagem…\n”;
switch (avaria) {
case 0: cout << “tudo em ordem!\n”;
break;
case 1: throw AvariaLigeira();
break;
case 2: throw AvariaGrave();
break;
}
}
catch (AvariaLigeira & e) {
cout << e.what() << “…resolvida no local\n”;
}
catch (AvariaGrave & e) {
cout << e.what() << “…rebocar para a oficina\n”;
throw; //relançar a excepção
}
cout << “…fim de etapa!\n”;
}
int main() {
try {
assistenciaEmViagem(0);
assistenciaEmViagem(1);
assistenciaEmViagem(2); //chamada à função que termina em erro
assistenciaEmViagem(0);
}
catch (AvariaGrave& e) {
cout << “\nmain()-> ” << e.what() << ” -> Avaria reparada na oficina\n” << ” -> canceladas etapas seguintes\n”;
}
}
………..Smart pointers
Permitem gerir de forma mais fácil e segura objectos em memória dinâmica.
Encapsulam ponteiros de tipo primitivo.
tres tipos:
unique_ptr (posso exclusiva do objecto apontado)
shared_ptr (objectos apontados por mais do que um ponteiro)
weak_ptr ()
exemplo de unique_ptr:
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <regex>
#include <initializer_list>
#include <fstream>
using namespace std;
class Produto
{
string nome;
int preco;
public:
Produto(string nome0, const int preco0)
: nome(nome0),
preco(preco0)
{
cout << ” construtor ” << getAsString() << endl;
}
~Produto()
{
cout << ” destrutor ” << getAsString() << endl;
}
string getAsString() const
{
ostringstream oss;
oss << nome + ” preco: ” + to_string(preco);
return oss.str();
}
};
int main()
{
//smart pointer
unique_ptr<Produto> prod = make_unique<Produto>(“Arroz”,1);
//make_unique cria o objecto em mem dinamica
//make_unique cria o smart_pointer na stack
cout << prod->getAsString();
//versao2, dá erro porque não tem construtor por cópia nem atribuir
//unique_ptr<Produto> prod2 = make_unique<Produto>(“Maria”, 22);
//unique_ptr<Produto> prod3(prod2);
//cout << prod2->getAsString();
cout << “\nfim do main” << endl;
}
mas o unique_ptr Não dispõe de:
Construtor por cópia
Operador atribuição
o unique_ptr pode ser movido mas não copiado, existe um construtor move
exemplo de unique_ptr semantica move:
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <regex>
#include <initializer_list>
#include <fstream>
using namespace std;
class Produto
{
string nome;
int preco;
public:
Produto(string nome0, const int preco0)
: nome(nome0),
preco(preco0)
{
cout << ” construtor ” << getAsString() << endl;
}
~Produto()
{
cout << ” destrutor ” << getAsString() << endl;
}
string getAsString() const
{
ostringstream oss;
oss << nome + ” preco: ” + to_string(preco);
return oss.str();
}
};
unique_ptr<Produto> cria_Produto() {
unique_ptr<Produto> local_ptr = make_unique<Produto>(“Bolachas”, 2);
return local_ptr; // local_ptr entrega a posse do objecto
}
void f() {
cout << “constr move:\n”;
unique_ptr<Produto> p1(cria_Produto()); // constructor move,
// recebe um objeto retornado por valor, um rvalue
// p1 tem agora a posse do objecto
cout << “atribuicao move:\n”;
unique_ptr<Produto> p2; // constructor por omissão, p2 não possui nada
p2 = cria_Produto(); // atribuição move
// o segundo membro da atribuicao é um objeto retornado por valor, um rvalue
// p2 tem agora a posse do objecto, o segundo objecto criado
}
int main()
{
unique_ptr<Produto> a = make_unique<Produto>(“Cafe”, 2); // a possui um objecto Produto
unique_ptr<Produto> b; // b não possui nada
// invocar atribuição move explicitamente
b = std::move(a); // move retorna uma referência rvalue para a
// funciona o operador atribuição move
// agora b possui o objecto Produto, a não possui nada
// invocar construção move explicitamente
unique_ptr<Produto> c(std::move(b)); // move retorna uma referência rvalue para b
cout << b->getAsString();
cout << “\nfim do main” << endl;
}