Tag: poo_pt4
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;
}