Para ser direto: em C, as macros são úteis para definir constantes, definir rotinas e para compilação condicional. Em C++, somente a terceira situação é válida.
Vamos ao exemplo de uma macro válida em C++:
#define CONSOLE_DEBUG
…
float x = sin(y);
#if defined (CONSOLE_DEBUG )
printf(“Valor do seno de y: %f\n”, x );
#endif
E é isso. Não utilize em C++ macros para definir constantes ou rotinas. Se for programar em C, utilize-as com cuidado.
Vamos a exemplos de problemas com macros.
Queremos uma macro que calcule o cubo de um número. Ela pode ser facilmente definida como:
#define cubo( A ) A*A*A
Quando, no código, você fizer:
int y = cubo(3);
Seu código será substituído por:
int y = 3*3*3;
Alá! y vale 27! Funcionou perfeitamente!
Aí em outra parte do código você faz:
int x = 2;
int y = cubo( x + 1 );
Infelizmente o resultado de y será 7. Isso acontece porque seu código será substituído por:
int y = x + 1 * x + 1 * x + 1;
Sabemos desde criança que a multiplicação tem precedência sobre a soma, e por esta razão o resultado não foi o esperado.
Uma versão melhorada desta macro seria:
#define cubo( A ) (A)*(A)*(A)
Ok, agora o exemplo do x + 1 funciona, mas e o seguinte caso?
int y = 27 / cubo( 3 )
Esperamos que o resultado seja 1, mas infelizmente, como dizem, a estrada do inferno é pavimentada de boas intenções. Seu y valerá 81!
É simples:
27 / (3) * (3) * (3)
27/3 será executado primeiro, e o resultado disso será multiplicado por 3 duas vezes, chegando ao incrível valor de 81. Precisamos de mais parêntesis. Vamos lá:
#define cubo( A ) ( (A) * (A) * (A) )
E finalmente chegamos a macro perfeita! Bom, quase. Alguém pode fazer (e com certeza fará!) alguma coisa do tipo:
int x = 2;
int y = cubo( ++x );
Esperamos que y valha 27 no fim das contas, mas você já deve ter percebido a bosta aqui:
int y = ( ( ++x ) * ( ++x ) * ( ++x ) );
No final x valerá 5 e y valerá alguma coisa que conhecemos como undefined behavior. (http://renangreinert.blogspot.com/2011/08/x-x.html).
Infelizmente, se você programa em C, terá que conviver com este risco. Se você programa em C++, evite todos estes problemas utilizando funções inline em vez de macros. O exemplo acima seria melhor escrito na seguinte forma:
inline int cubo( int num )
{
return num * num * num;
}
}
Quer que cubo valha para outros valores, como float, double, char? Utilize template:
template < typename T >
inline T cubo( T num )
{
return num * num * num;
}
E assim ficamos todos felizes! =D
Outro exemplo de problemas com rotinas em macros são as que têm mais de 1 linha.
#define imprime \
printf( "1\n" ); \
printf( "2\n" ); \
printf( "3\n" );
…
for ( int i = 0; i < 10; ++i )
imprime;
Note que apenas a primeira linha da macro (isto é, a que imprime 1) será executada 10 vezes. 2 e 3 serão impressos apenas uma vez, no fim do for.
Bom, é uma boa prática sempre fechar os fors, ifs, whiles, etc com chaves, mesmo que tenham apenas 1 linha, porém como muita gente não segue isso, é melhor fechar as suas macros também. Assim:
#define imprime \
{ \
printf( "1\n" ); \
printf( "2\n" ); \
printf( "3\n" ); \
}
Aí está um exemplo de uma macro sadia de múltiplas linhas em C (nem preciso mencionar que em C++ você deve utilizar funções inline, né?).
Ok, e os #defines que definem valores, e não rotinas?
Vamos ao exemplo:
#define MAX_VALUE 10
Esta abordagem possui vários problemas. O primeiro é que você não sabe o que é “10”. Pode ser um int, um char, um short ou até mesmo um float mal definido (a definição correta seria 10.0F).
Este problema pode ser facilmente resolvido com um cast:
#define MAX_VALUE (int)10
Melhor, mas ainda assim tem seus problemas. Suponha que seu programa calcula a pressão, a temperatura e a velocidade. MAX_VALUE é o valor máximo do quê realmente?
A única forma de saber é olhando a definição e torcer para que tenha algum comentário ou coisa do tipo.
Você pode desenvolver um padrão para definição das macros, algo do tipo PRESSAO_MAX_VALUE, VELOC_MAX_VALUE, TEMPERATURA_MAX_VALUE, etc, mas quando você trabalha com projetos muito grandes isso começa a encher o saco.
Outro problema com #defines é que se você definir MAX_VALUE em uma parte do código com 10 e em outra parte do código como sendo 20, seu compilador dará um warning, mas só. Como (infelizmente) muitas pessoas tem o hábito de ignorar warnings, isso será um problema.
Se você utilizar variáveis do tipo const em C++, você estará livre destes problemas.
No exemplo citado, você poderia ter:
class Pressao
{
{
public:
static int MAX_VALUE = 10;
...
};
};
class Temperatura
{
public:
static int MAX_VALUE = 30;
...
};
};
class Velocidade
{
public:
static int MAX_VALUE = 100;
...
};
};
No código, você poderia utilizar:
int x = Velocidade::MAX_VALUE;
int y = Temperatura::MAX_VALUE;
e por aí vai.
Melhor, né?
É triste, mas sempre tem um porém. A versão atual do C++ só permite que tipos com valores inteiros (int, char, short...) sejam definidos na declaração dentro de classes. Assim, floats, doubles e strings estão de fora. A nova versão do C++ (a C++0x, se você ainda não ouviu falar é bom se atualizar) promete resolver este problema, mas enquanto ela não vem, vamos as alternativas.
Uma é definir as variáveis dentro do namespace. Algo do tipo:
namespace nsPressao
{
const float MAX_VALUE = 10.5F;
class Pressao
{
...
};
}
}
Outra alternativa seria declarar na classe e definir fora:
//Pressao.h
class Pressao{
public:
static const float MAX_VALUE;
};
};
e no começo do Pressao.cpp definir a variável:
//Pressao.cpp
const float Pressao::MAX_VALUE = 10.5F;
Pressao::Pressao()
...
E é isso. Espero ter convencido a todos de que macros em C++ não são uma boa opção! Abraço e bom fim de semana!