Definitivamente, todos nós usamos typedef
s #define
es uma vez ou outra. Hoje, enquanto trabalhava com eles, comecei a pensar em algo.
Considere as 2 situações abaixo para usar o int
tipo de dados com outro nome:
typedef int MYINTEGER
e
#define MYINTEGER int
Como na situação acima, em muitas situações, podemos muito bem realizar algo usando #define e também o mesmo usando typedef, embora as maneiras pelas quais fazemos o mesmo possam ser bem diferentes. #define também pode executar ações MACRO que um typedef não pode.
Embora a razão básica para usá-los seja diferente, qual é o nível de trabalho deles? Quando um deve ser preferido em relação ao outro, quando ambos podem ser usados? Além disso, é garantido que um é mais rápido que o outro em que situações? (por exemplo, #define é diretiva de pré-processador, portanto, tudo é feito muito mais cedo do que na compilação ou no tempo de execução).
fonte
Respostas:
A
typedef
é geralmente preferido, a menos que haja algum motivo estranho para você precisar especificamente de uma macro.macros fazem substituição textual, o que pode causar considerável violência à semântica do código. Por exemplo, dado:
você poderia escrever legalmente:
porque se
short MYINTEGER
expande parashort int
.Por outro lado, com um typedef:
o nome
MYINTEGER
é outro nome para o tipoint
, não uma substituição textual para a palavra-chave "int".As coisas pioram com os tipos mais complicados. Por exemplo, dado o seguinte:
a
,b
Ec
são todos os ponteiros, masd
é umchar
, porque a última linha se expande para:que é equivalente a
(Typedefs para tipos de ponteiros geralmente não são uma boa ideia, mas isso ilustra o ponto.)
Outro caso estranho:
fonte
typedef
, mas a maneira correta de construir uma macro que faz algo com o que se segue é a utilização de argumentos:#define CHAR_PTR(x) char *x
. Pelo menos isso fará com que o compilador kvetch se usado incorretamente.const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
De longe, o maior problema das macros é que elas não têm escopo definido. Isso por si só garante o uso de typedef. Além disso, é mais claro semanticamente. Quando alguém que lê seu código vê uma definição, ele não saberá do que se trata até que leia e compreenda toda a macro. Um typedef diz ao leitor que um nome de tipo será definido. (Devo mencionar que estou falando sobre C ++. Não tenho certeza sobre o escopo de typedef para C, mas acho que é semelhante).
fonte
O código fonte é escrito principalmente para colegas desenvolvedores. Os computadores usam uma versão compilada.
Nesta perspectiva,
typedef
carrega um significado que#define
não.fonte
A resposta de Keith Thompson é muito boa e, juntamente com os pontos adicionais de Tamás Szelei sobre o escopo, devem fornecer todo o histórico necessário.
Você deve sempre considerar as macros o último recurso dos desesperados. Há certas coisas que você só pode fazer com macros. Mesmo assim, você deve estar pensando muito, se realmente quiser. A quantidade de problemas na depuração que pode ser causada por macros sutilmente quebradas é considerável e você terá que procurar em arquivos pré-processados. Vale a pena fazer isso apenas uma vez, para ter uma idéia do escopo do problema em C ++ - apenas o tamanho do arquivo pré-processado pode ser uma surpresa.
fonte
Além de todas as observações válidas sobre o escopo e a substituição de texto já fornecidas, #define também não é totalmente compatível com o typedef! Ou seja, quando se trata de ponteiros de função.
Isso declara um tipo
fptr_t
que é um ponteiro para uma função do tipovoid func(void)
.Você não pode declarar esse tipo com uma macro.
#define fptr_t void(*)(void)
obviamente não vai funcionar. Você teria que escrever algo obscuro#define fptr_t(name) void(*name)(void)
, o que realmente não faz sentido na linguagem C, onde não temos construtores.Os ponteiros de matriz também não podem ser declarados com #define:
typedef int(*arr_ptr)[10];
E, embora C não tenha suporte a idiomas para segurança de tipo, vale a pena mencionar, outro caso em que typedef e #define não são compatíveis é quando você faz conversões de tipo suspeitas. A ferramenta do compilador e / ou analisador astático poderá fornecer avisos para essas conversões se você usar o typedef.
fonte
char *(*(*foo())[])();
(foo
é uma função que retorna um ponteiro para uma matriz de ponteiros para funções que retornam o ponteiro parachar
).Use a ferramenta com a menor potência possível e a com mais avisos. #define é avaliado no pré-processador, você está sozinho por lá. typedef é avaliado pelo compilador. As verificações são feitas e o typedef pode definir apenas tipos, como o nome indica. Então, no seu exemplo, definitivamente escolha typedef.
fonte
Além dos argumentos normais contra define, como você escreveria essa função usando macros?
Obviamente, esse é um exemplo trivial, mas essa função pode somar qualquer sequência iterável direta de um tipo que implemente adição *. Typedefs usados assim são um elemento importante da Programação Genérica .
* Acabei de escrever isso aqui, deixo de compilá-lo como um exercício para o leitor :-)
EDITAR:
Essa resposta parece estar causando muita confusão, então deixe-me extrair mais. Se você olhar dentro da definição de um vetor STL, verá algo semelhante ao seguinte:
O uso de typedefs nos contêineres padrão permite que uma função genérica (como a que eu criei acima) faça referência a esses tipos. A função "Soma" é modelada no tipo de contêiner (
std::vector<int>
), não no tipo mantido dentro do contêiner (int
). Sem typedef, não seria possível fazer referência a esse tipo interno.Portanto, typedefs são centrais para o C ++ moderno e isso não é possível com macros.
fonte
typedef
, não macros vs funções embutidas.typedef
se encaixa na filosofia C ++: todas as verificações / declarações possíveis em tempo de compilação.#define
é apenas um truque de pré-processador que esconde muita semântica para o compilador. Você não deve se preocupar mais com o desempenho da compilação do que com a correção do código.Quando você cria um novo tipo, está definindo uma nova "coisa" que o domínio do seu programa manipula. Portanto, você pode usar essa "coisa" para compor funções e classes e usar o compilador para seu benefício, para fazer verificações estáticas. De qualquer forma, como o C ++ é compatível com o C, há muitas conversões implícitas entre
int
e tipos baseados em int que não geram avisos. Portanto, você não recebe todo o poder das verificações estáticas neste caso. No entanto, você pode substituir o seutypedef
por umenum
para encontrar conversões implícitas. Por exemplo: se você tivertypedef int Age;
, poderá substituir porenum Age { };
e receberá todos os tipos de erros por conversões implícitas entreAge
eint
.Outra coisa: o
typedef
pode estar dentro de umnamespace
.fonte
Usar define em vez de typedefs é preocupante sob outro aspecto importante, a saber, o conceito de características de tipo. Considere classes diferentes (pense em contêineres padrão), que definem seus typedefs específicos. Você pode escrever código genérico consultando os typedefs. Exemplos disso incluem os requisitos gerais de contêiner (padrão c ++ 23.2.1 [container.requirements.general]) como
Tudo isso não é expressável de maneira genérica com uma macro porque não tem escopo definido.
fonte
Não esqueça as implicações disso no seu depurador. Alguns depuradores não tratam #define muito bem. Brinque com os dois no depurador que você usa. Lembre-se de que você passará mais tempo lendo do que escrevendo.
fonte
"#define" substituirá o que você escreveu depois, typedef criará o tipo. Portanto, se você deseja ter um tipo de cliente - use typedef. Se você precisar de macros - use define.
fonte