typedefs e #defines

30

Tutti abbiamo definitivamente usato typedef se #define s una volta o l'altra. Oggi mentre lavoravo con loro, ho iniziato a meditare su una cosa.

Considera le seguenti 2 situazioni per utilizzare il tipo di dati int con un altro nome:

typedef int MYINTEGER

e

#define MYINTEGER int

Come la situazione di cui sopra, possiamo, in molte situazioni, realizzare molto bene una cosa usando #define, e anche fare lo stesso usando typedef, anche se i modi in cui facciamo lo stesso possono essere molto diversi. #define può anche eseguire azioni MACRO che un typedef non può.

Sebbene il motivo fondamentale per utilizzarli sia il diverso, quanto è diverso il loro funzionamento? Quando si dovrebbe essere preferito all'altro quando entrambi possono essere usati? Inoltre, è garantito essere più veloce dell'altro in quali situazioni? (ad esempio, #define è una direttiva per il preprocessore, quindi tutto è fatto molto prima che alla compilazione o al runtime).

    
posta c0da 18.01.2012 - 12:06
fonte

11 risposte

65

Un typedef è generalmente preferito a meno che non vi sia qualche strana ragione per cui hai specificamente bisogno di una macro.

Le macro

eseguono la sostituzione testuale, che può causare una notevole violenza alla semantica del codice. Ad esempio, dato:

#define MYINTEGER int

potresti scrivere legalmente:

short MYINTEGER x = 42;

perché short MYINTEGER si espande in short int .

D'altra parte, con typedef:

typedef int MYINTEGER:

il nome MYINTEGER è un altro nome per il tipo int , non una sostituzione testuale per la parola chiave "int".

Le cose vanno ancora peggio con tipi più complicati. Ad esempio, dato questo:

typedef char *char_ptr;
char_ptr a, b;
#define CHAR_PTR char*
CHAR_PTR c, d;

a , b e c sono tutti puntatori, ma d è un char , perché l'ultima riga si espande in:

char* c, d;

che è equivalente a

char *c;
char d;

(I typedef per i tipi di puntatore di solito non sono una buona idea, ma questo illustra il punto).

Un altro caso strano:

#define DWORD long
DWORD double x;     /* Huh? */
    
risposta data 18.01.2012 - 12:17
fonte
15

Di gran lunga, il problema più grande con le macro è che non hanno ambito. Questo da solo garantisce l'uso di typedef. Inoltre, è più chiaro semanticamente. Quando qualcuno che legge il tuo codice vede una definizione, non saprà di cosa si tratta finché non lo leggerà e comprenderà l'intera macro. Un typedef dice al lettore che verrà definito un nome di tipo. (Dovrei menzionare che sto parlando di C ++. Non sono sicuro riguardo al typedef scoping per C, ma immagino sia simile).

    
risposta data 18.01.2012 - 12:18
fonte
15

Il codice sorgente è scritto principalmente per i colleghi sviluppatori. I computer usano una versione compilata di esso.

In questa prospettiva, typedef ha un significato che #define non ha.

    
risposta data 18.01.2012 - 12:44
fonte
6

La risposta di Keith Thompson è molto buona e insieme a I punti aggiuntivi di Tamás Szelei sull'ambito dell'ambito dovrebbero fornire tutto lo sfondo di cui hai bisogno.

Dovresti sempre considerare i macro l'ultima risorsa dei disperati. Ci sono alcune cose che puoi fare solo con le macro. Anche allora, dovresti pensare a lungo e duramente se vuoi davvero farlo. La quantità di dolore nel debug che può essere causato da macro sottilmente spezzate è considerevole, e avrai la possibilità di esaminare i file preelaborati. Vale la pena farlo una sola volta, per avere un'idea dell'ambito del problema in C ++: solo le dimensioni del file preelaborato potrebbero aprire gli occhi.

    
risposta data 18.01.2012 - 12:21
fonte
5

Oltre a tutte le osservazioni valide sull'ambito e sulla sostituzione del testo già fornite, #define non è completamente compatibile con typedef! Vale a dire quando si tratta del caso dei puntatori di funzione.

typedef void(*fptr_t)(void);

Questo dichiara un tipo fptr_t che è un puntatore a una funzione del tipo void func(void) .

Non puoi dichiarare questo tipo con una macro. #define fptr_t void(*)(void) ovviamente non funzionerà. Dovresti scrivere qualcosa di oscuro come #define fptr_t(name) void(*name)(void) , che in realtà non ha alcun senso nel linguaggio C, dove non abbiamo costruttori.

I puntatori di array non possono essere dichiarati con #define: typedef int(*arr_ptr)[10];

E anche se C non ha supporto linguistico per la sicurezza del tipo che merita di essere menzionato, un altro caso in cui typedef e #define non è compatibile quando si fanno conversioni di tipo sospette. Lo strumento di analisi del compilatore e / o dell'analizzatore astatico potrebbe essere in grado di fornire avvertimenti per tali conversioni se si utilizza typedef.

    
risposta data 19.01.2012 - 08:48
fonte
4

Usa lo strumento con la minima potenza per completare il lavoro e quello con la maggior parte degli avvisi. #define è valutato nel preprocessore, tu sei in gran parte da solo lì. typedef è valutato dal compilatore. I controlli sono forniti e typedef può solo definire i tipi, come dice il nome. Quindi nel tuo esempio scegli sicuramente typedef.

    
risposta data 18.01.2012 - 12:15
fonte
2

Oltre i normali argomenti contro define, come scriveresti questa funzione usando Macro?

template <typename IterType>
typename IterType::value_type Sum(
    const IterType& begin, 
    const IterType& end, 
    const IterType::value_type& initialValue)
{
    typename IterType::value_type result = initialValue;
    for (IterType i = begin; i != end; ++i)
        result += i;

    return result;
}

....

vector<int> values;
int sum = Sum(values.begin(), values.end(), 0);

Questo è ovviamente un esempio banale, ma quella funzione può sommare qualsiasi sequenza iterabile in avanti di un tipo che implementa l'addizione *. I typedef utilizzati in questo modo sono un elemento importante di Programmazione generica .

* Ho appena scritto questo qui, lascio la compilazione come esercizio per il lettore: -)

EDIT:

Questa risposta sembra causare molta confusione, quindi permettimi di disegnarla di più. Se si dovesse guardare all'interno della definizione di un vettore STL, si vedrebbe qualcosa di simile al seguente:

template <typename ValueType, typename AllocatorType>
class vector
{
public:
    typedef ValueType value_type;
...
}

L'uso di typedef all'interno dei contenitori standard consente una funzione generica (come quella che ho creato sopra) per fare riferimento a questi tipi. La funzione "Somma" è basata sul tipo del contenitore ( std::vector<int> ), non sul tipo contenuto nel contenitore ( int ). Senza typedef non sarebbe possibile fare riferimento a quel tipo interno.

Pertanto, typedef sono fondamentali per Modern C ++ e questo non è possibile con Macro.

    
risposta data 19.01.2012 - 13:57
fonte
1

typedef si adatta alla filosofia C ++: tutti i possibili assegni / asserzioni in fase di compilazione. #define è solo un trucco del preprocessore che nasconde un sacco di semantica al compilatore. Non dovresti preoccuparti delle prestazioni della compilazione più della correttezza del codice.

Quando crei un nuovo tipo, definisci una nuova "cosa" che il dominio del tuo programma manipola. Quindi puoi usare questa "cosa" per comporre funzioni e classi, e puoi usare il compilatore a tuo vantaggio per fare controlli statici. Ad ogni modo, poiché C ++ è compatibile con C, ci sono molte conversioni implicite tra int e tipi basati su int che non generano avvisi. Quindi in questo caso non ottieni tutta la potenza dei controlli statici. Tuttavia, puoi sostituire typedef con enum per trovare conversioni implicite. Ad esempio: se hai typedef int Age; , puoi sostituire con enum Age { }; e otterrai tutti i tipi di errori da conversioni implicite tra Age e int .

Un'altra cosa: il typedef può trovarsi all'interno di un namespace .

    
risposta data 18.01.2012 - 17:31
fonte
1

L'uso di define al posto di typedef è problematico sotto un altro aspetto importante, e precisamente il concetto di caratteri tipografici. Considerare classi diverse (si pensi ai contenitori standard) che definiscono tutti i loro typedef specifici. Puoi scrivere codice generico facendo riferimento al typedefs. Esempi di questo includono i requisiti generali del contenitore (c ++ standard 23.2.1 [container.requirements.general]) come

X::value_type
X::reference
X::difference_type
X::size_type

Tutto ciò non è espressivo in modo generico con una macro perché non è ad ambito.

    
risposta data 18.01.2012 - 19:09
fonte
1

Non dimenticare le implicazioni di questo nel tuo debugger. Alcuni debugger non gestiscono molto bene #define. Gioca con entrambi nel debugger che usi. Ricorda, passerai più tempo a leggerlo che a scriverlo.

    
risposta data 21.01.2012 - 02:48
fonte
-2

"# define" sostituirà ciò che hai scritto dopo, typedef creerà il tipo. Quindi, se vuoi avere un tipo di custome, usa typedef. Se hai bisogno di macro, usa define.

    
risposta data 18.01.2012 - 16:29
fonte

Leggi altre domande sui tag