Enviado em 09/07/2015 - 08:42h
Alguém pode me explicar o conceito de Setters e Getters?
Enviado em 09/07/2015 - 08:42h
Enviado em 09/07/2015 - 09:38h
- Mas pq não usar ele como atributo publico?
class Classe{
int x;
public:
void setX(int x){this->x = x;}
int getX(){return this->x;}
};
Ou outro exemplo
class Classe{
int x;
public:
void setX(int x){
if(x!=5){
this->x = x;
}else{
std::cout << "ERRO! Variavel X nao pode ser 5!" << std::endl;
return;
}
}
int getX(){return this->x;}
};
Espero ter ajudado
class Carro{
int numRodas;
public:
void setRodas(int numRodas){
if(numRodas==4){
this->numRodas =numRodas;
}else{
std::cout << "ERRO! Variavel \"numRodas\" nao pode ser diferente de 4!" << std::endl;
return;
}
}
int getRodas(){return this->numRodas;}
};
Enviado em 09/07/2015 - 11:55h
Enviado em 10/07/2015 - 19:52h
class A {
public:
int value;
};
int main(){
A *a=new A; // Ponteiro, só para o exemplo ficar mais drástico.
int &r=a->value;
int *p=&a->value;
a->value=5; // Acesso via objeto. OK.
r++; // Altera objeto sem mencioná-lo diretamente (mau!).
(*p)*=2; // Idem (via ponteiro) (mau!).
delete a; // NOTE BEM: Removi o objeto...
r++; // ... mas ainda estou modificando alguma coisa...
(*p)*=2; // ... que nem existe mais! (mau, muito mau!)
{
A aa; // Agora, sem ponteiro.
p=&aa.value; // Mas olha a maldade aqui.
}
// Neste ponto, aa não existe mais, mas p ainda ponta para o que seria parte dele.
(*p)++; // BOOM!
} A questão do encapsulamento é particularmente relevante com objetos compostos por vários elementos internos, quando a manutenção da coerência entre esses elementos internos é vital para que o objeto seja consistente. Na verdade, classes bem implementadas têm de garantir que todos os objetos estejam completamente consistentes durante todo o seu tempo de vida, desde que ele termina de ser construído até o momento em que será des(cons)truído (e mesmo durante a construção e desconstrução, as coisas também deve ser feitas com ordem, e eventuais erros tratados de forma a manter o estado geral do programa consistente).#include <fstream>A classe c_str_wrapper poderia ser melhorada, do seguinte modo.
#include <iostream>
#include <string>
class c_str_wrapper {
public: /* Deveria ser "private:", mas é para ilustrar o perigo. */
std::string str;
bool is_valid;
public:
c_str_wrapper(const char *s){
if(s){
str=s;
is_valid=true;
}
else
is_valid=false;
}
// Os operadores de conversão de tipos, abaixo, são
// casos particulares de getters, mas, sozinhos, não
// garantem a consistência dos membros internos do
// objeto.
operator bool(){ return is_valid; }
operator const char *() const {
return is_valid? str.c_str(): nullptr;
}
};
bool print(const char *p){
std::cout << (p? (*p? p: "[[EMPTY_STRING]]"): "[[NULL_POINTER]]") << '\n';
return std::cout;
}
int main(){
std::cout.exceptions(std::fstream::failbit|std::fstream::badbit);
c_str_wrapper a{nullptr};
c_str_wrapper b{""};
c_str_wrapper c{"Teste"};
print(a); // Imprime "[[NULL_POINTER]]".
print(b); // Imprime "[[EMPTY_STRING]]".
print(c); // Imprime "Teste".
// Até aqui, tudo OK. A semântica está como esperado. Agora vamos
// começar a esculhambar.
a.is_valid=true;
b.is_valid=false;
c.is_valid=false;
print(a); // Imprime "[[EMPTY_STRING]]" porque casualmente o construtor
// default de std::string coloca um string vazio em a.str,
// mesmo quando ele, pela semântica original, não devesse ser
// usado.
print(b); // Imprime "[[NULL_POINTER]]".
print(c); // Imprime "[[NULL_POINTER]]".
const char *cp_a, *cp_b, *cp_c; // Ponteiros para caracteres constantes.
a.is_valid=b.is_valid=c.is_valid=true; // Todas as strings "válidas".
cp_a=a;
cp_b=b;
cp_c=c;
// Uso const_cast para burlar não só o encapsulamento de c_str_wrapper,
// mas também o de std::string (std::string::c_str() não é naturalmente
// muito segura, mas é algo que se tem de sofrer para poder ter compa-
// tibilidade com strings do C; ela pelo menos retorna um ponteiro para
// constantes, mas casts sempre permitem que a gente transforme qualquer
// coisa em outra).
char *p_a, *p_b, *p_c;
p_a=const_cast<char *>(cp_a);
p_b=const_cast<char *>(cp_b);
p_c=const_cast<char *>(cp_c);
p_a[0]='x'; // Removo o const e sobrescrevo o '\0' do fim da string
// do C usada internamente por std::string.
p_b[0]='y'; // Removo o const e sobrescrevo o '\0'.
p_c[5]='z'; // Idem (só que na sexta posição).
print(a); // Pode imprimir 'x' ou 'y', talvez seguido de lixo, mas pode
// também acontecer qualquer coisa, até capotar o programa.
print(b); // Idem.
print(c); // Pode imprimir "Testez", talvez seguido de lixo, mas pode
// também acontecer qualquer coisa, até capotar o programa.
// Mais "divertido" ainda: mexo em memória fora do espaço alocado pelo
// construtor de std::string para conter textos ou a representação com-
// patível com a do C.
p_a[10]='a';
p_b[10]='b';
p_c[10]='c';
// Mesmo que uma das operações acima não dê pau, pode ser que dê problema
// na hora de chamar um dos destrutores, o que acontece aqui.
}
class c_str_wrapper {
private: /* Não mais "public:". */
std::string str;
bool is_valid;
public:
// Setter que dá ao objeto o sentido de ponteiro nulo.
// Note que ele mexe em mais do que apenas um campo.
void set_null(){
str.clear();
is_valid=false;
}
// Outro setter, que recebe C-string. Note que ele também
// tem de ser esperto.
void set_str(const char *s){
if(s){
str=s;
is_valid=true;
}
else{
str.clear();
is_valid=false;
}
}
// O setter acima é tão adequado ao objeto que o próprio
// construtor de conversão de tipos pode usá-lo.
c_str_wrapper(const char *s){ set_str(s); }
// O operador de conversão de tipo abaixo é um caso
// particular de getter. Uma função equivalente segue.
operator bool() const { return is_valid; }
bool is_null() const { return is_valid; }
// Getter da string, que retorna uma cópia de str ou, se
// o dado não for considerado válido, gera uma exceção
// (note que o tipo de retorno é “std::string”, e não
// “std::string &”).
std::string get_str() const {
if(!is_valid)
throw std::runtime_error("Attempt to get an undefined string");
return str;
}
// O getter especial dado pela conversão de tipos para
// const char * foi removido. Em seu lugar, deve-se usar algo
// como “obj.get_str.c_str()”.
// Outro tipo especial de getter, que coloca uma cópia num
// array do C.
void get_str(char *dst, size_t dst_size) const {
if(!is_valid)
throw std::runtime_error("Attempt to get an undefined string");
if(!dst)
throw std::invalid_argument("Attempt to write to null pointer");
if(dst_size<=str.length())
throw std::range_error("Destination buffer too small");
strcpy(dst, str.c_str());
}
};
// Também seria necessário escrever uma nova versão de print().
bool print(const c_str_wrapper &s){
if(!s) // getter implícito de conversao para bool
std::cout << "[[NULL_POINTER]]";
else{
std::string str(s.get_str());
std::cout << (s.empty()? "[[EMPTY_STRING]]": s);
}
return std::cout << '\n';
} A eliminação de uma das conversões de tipos é compensada pelos outros getters colocados.int main(){
// Para maior dramatismo, uso o qualificador const para os objetos.
const c_str_wrapper a{"Teste"};
const c_str_wrapper b{a};
const c_str_wrapper c{a};
const_cast<char *>(a.get_str().c_str())[0]='P';
// Corremos o risco de imprimir "Peste" até três vezes
// (e de fato, é o que ocorre na minha máquina)!
print(a);
print(b);
print(c);
} Esse efeito pode ser contornado por meio de substitutos para o construtor de cópia para o operador de atribuição gerados implicitamente para a classe pelo compilador. Essas versões explícitas devem forçar a geração de um objeto std::string totalmente novo, em lugar de usar a atribuição que usa o recurso de copy-on-write de um objeto string preexistente.// Construtor de cópia.Com esses novos membros na classe, o programa anterior mexe apenas com o conteúdo de a.
c_str_wrapper(const c_str_wrapper &other) :
str(other.str.c_str()), // Note que eu NÃO fiz “str(other.str)”!!
is_valid(other.is_valid)
{
}
// Operador de atribuição.
c_str_wrapper &operator=(const c_str_wrapper &other){
str=other.str.c_str(); // De novo: eu NÃO fiz “str=other.str”!!
is_valid=other.is_valid;
return *this;
}
Entre na sua conta para responder.