Como implementar facilmente getters e setters em C++
Se você é como eu que odeia ficar digitando dois métodos para cada atributo que você cria em sua classe, seus problemas acabaram.
Trata-se de uma macro feita através de um #define mesmo que facilita muito a vida dos programadores OO em C++.
Vejamos a macro:
#define declare(type, name) \
private: type _##name; \
public: \
void set##name(const type& var) \
{\
_##name = var;\
}\
\
type get##name() \
{\
return _##name; \
}\
E eis a maneira de usar:
#include < iostream >
using namespace std;
class Teste
{
declare(int, x); //declarando um atributo x do tipo int e seu método get e set
declare(int, y); //declarando um atributo y do tipo int e seu método get e set
};
int main()
{
Teste t;
t.setx(2);
t.sety(3);
cout << " "<< t.gety() << endl;
}
Como isso funciona?
É simples, na linha 2 da macro declaramos uma variável de acesso private
com tipo type e nome _name. Ou seja, ao chamarmos declare(int, x), será criado um atributo private int _x;. Nas linhas a seguir declaramos os métodos de acesso void setname(const type var) e type getname() que, no caso do nosso exemplo declare(int, x), se transformará em:
void setx(const int& var)
{
_x = var;
}
int getx()
{
return _x;
}
Eis o código final da classe Teste:
class Teste
{
private: int _x;
public:
void setx(const int& var)
{
_x = var;
}
int getx()
{
return _x;
}
private: int _y;
public:
void sety(const int& var)
{
_y = var;
}
int gety()
{
return _y;
}
};
Lembrando que vale para qualquer tipo. Assim podemos declarar por exemplo declare(std::string, nome)!
É isso aí… Até a próxima!
Só para esclarecer, conversando com alguns membros da comunidade C e C++ Brasil do Orkut, o membro Paulo lembrou de colocar o parâmetro passado para o setter como referência para evitar overhead e que esse tipo de declaração não funciona com qualquer tipo de dado no C++.
Comentário completo:
“Na sua macro para getters e setters, eu faria o setter receber uma referência constante (const type &valor), o que pode ser útil para não entupir a pilha quando o tipo type for um tipo não-nativo, como algo baseado em std::vector.
Por outro lado, este é um tipo de macro que alguns consideram perigoso, pois reinventa a sintaxe. Note como a declaração fica disfarçada de chamada de função, podendo induzir as pessoas a um tipo de “oba! vou usar isso fora de classes também”.
Tem ainda outro problema: não funciona com qualquer tipo — arrays, por exemplo, dariam erro.”
Queri saber o porquê das “\” e das “##”. Tem como explicar?
Andre Brito
quinta-feira 21 \21\UTC maio \21\UTC 2009 em 13:50
Claro Andre!!!
As “\” são escapes de linha. Servme para poder escrever o #define em várias linhas como se fosse em uma só.
As “##” servem para concatenar. como em set##name, se fosse setname, o pré-processador não substituiria o name. Com os ##, ele escreve o set e concatena com o valor de name.
Um abraço!
Murilo Adriano
quinta-feira 21 \21\UTC maio \21\UTC 2009 em 16:13
Hmm… interessante isso ein! Se soubesse disso uns anos atrás, teria em poupado muito trabalho nos códigos de ED e similares ><"
Eduardo
quinta-feira 21 \21\UTC maio \21\UTC 2009 em 17:17
Pena que se usarmos essas artimanhas em ED, o professor logo vai encrespar! hehehe
Murilo Adriano
quinta-feira 21 \21\UTC maio \21\UTC 2009 em 21:24
Murilo,
Valeu pela explicação cara. Animal isso.
Abraço.
Andre Brito
quinta-feira 21 \21\UTC maio \21\UTC 2009 em 23:29
basicamente, como criar getters e setters INÚTEIS em C++…
O propósito de se criar esses métodos é justamente a possibilidade de se modificar a forma como se obtem um valor na interface da classe… declarar desse jeito não te dá nenhuma vantagem sobre acessar diretamente o valor do atributo, e ainda adiciona o overhead da chamada de método.
Bad.
Edric Garran
segunda-feira 5 \05\UTC dezembro \05\UTC 2011 em 13:53
Olá Edric,
bom, não acho que seja totalmente inútil pois o intuito do post foi mostrar como evitar reescrever coisas repetitivas, nesse caso getters e setters padrões.
Concordo com você que pode ser feito basicamente expondo as variáveis-membro da classe para que possam ser acessadas diretamente. Porém declará-las como privadas nos dá a vantagem de poder alterar o modo em que modificamos essas variáveis com o menor esforço possível (alterando apenas a função-membro get/set apropriadamente) e não em cada lugar onde uma atribuição é feita. Então há uma utilidade visando uma maior manutenibilidade de código.
Outra coisa que gostaria que notasse é que não é verdade que exista overhead de chamada de função-membro. Isso porque funções-membro declaradas e definidas dentro do corpo da classe são automaticamente inlined [1].
Not so bad!
Abraço.
[1] http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.8
Murilo Adriano
segunda-feira 5 \05\UTC dezembro \05\UTC 2011 em 14:15