Au cours de mes années de programmation en C++ (MFC), je n’ai jamais ressenti le besoin d’utiliser typedef
. Je ne sais donc pas vraiment à quoi cela sert. Où devrais-je l'utiliser? Existe-t-il des situations réelles dans lesquelles l’utilisation de typedef
est préférée? Ou est-ce vraiment plus un mot-clé spécifique à C?
typedef
est nécessaire pour plusieurs métaprogrammations de modèles tasks - chaque fois qu'une classe est traitée comme une "fonction de type à la compilation", une typedef
est utilisée comme "valeur de type à la compilation" pour obtenir type. Par exemple. Considérons une métafonction simple pour convertir un type de pointeur en son type de base:
template<typename T>
struct strip_pointer_from;
template<typename T>
struct strip_pointer_from<T*> { // Partial specialisation for pointer types
typedef T type;
};
Exemple: l'expression de type strip_pointer_from<double*>::type
est évaluée à double
. Notez que la métaprogrammation des modèles n'est pas couramment utilisée en dehors du développement de la bibliothèque.
typedef
est utile pour donner un alias court et précis à des types de pointeurs de fonction compliqués:
typedef int (*my_callback_function_type)(int, double, std::string);
void RegisterCallback(my_callback_function_type fn) {
...
}
Dans le livre de Bjarne, il indique que vous pouvez utiliser typedef pour traiter les problèmes de portabilité entre systèmes dotés de tailles entières différentes. (c'est une paraphrase)
Sur une machine où sizeof (int) est 4, vous pouvez
typedef int int32;
Ensuite, utilisez int32 partout dans votre code. Lorsque vous passez à une implémentation de C++ où sizeof (int) est 2, vous pouvez simplement changer le typdef
typedef long int32;
et votre programme fonctionnera toujours sur la nouvelle implémentation.
utiliser avec un pointeur de fonction
Masquer les déclarations de pointeur de fonction avec un typedef
void (*p[10]) (void (*)() );
Seuls quelques programmeurs peuvent dire que p est un "tableau de 10 pointeurs sur une fonction renvoyant void et prenant un pointeur sur une autre fonction qui retourne void et ne prend aucun argument". La syntaxe lourde est presque indéchiffrable. Cependant, vous pouvez le simplifier considérablement en utilisant des déclarations typedef. Commencez par déclarer une typedef pour "un pointeur sur une fonction renvoyant void et ne prenant aucun argument" comme suit:
typedef void (*pfv)();
Ensuite, déclarez un autre typedef pour "un pointeur sur une fonction retournant un vide et prenant un pfv" sur la base du typedef que nous avons précédemment déclaré:
typedef void (*pf_taking_pfv) (pfv);
Maintenant que nous avons créé le typedef pf_taking_pfv comme synonyme du "pointeur indéréglable" sur une fonction renvoyant la valeur vide et prenant un pfv ", déclarer un tableau de 10 de ces pointeurs est un jeu d'enfant:
pf_taking_pfv p[10];
Juste pour donner quelques exemples des choses dites: les conteneurs STL.
typedef std::map<int,Froboz> tFrobozMap;
tFrobozMap frobozzes;
...
for(tFrobozMap::iterator it=frobozzes.begin(); it!=map.end(); ++it)
{
...
}
Il n’est pas inhabituel d’utiliser même des typedefs comme
typedef tFrobozMap::iterator tFrobozMapIter;
typedef tFrobozMap::const_iterator tFrobozMapCIter;
Autre exemple: utilisation de pointeurs partagés:
class Froboz;
typedef boost::shared_ptr<Froboz> FrobozPtr;
[mise à jour] Comme par commentaire - où les mettre?
Le dernier exemple - en utilisant shared_ptr
- est simple: constitue un matériau d’en-tête réel - ou au moins un en-tête avant. De toute façon, vous avez besoin de la déclaration forward pour shared_ptr, et l'un de ses avantages déclarés est que son utilisation est sans danger avec un déclic.
En d'autres termes: s'il existe un shared_ptr, vous devez probablement utiliser le type uniquement via shared_ptr, la séparation des déclarations n'a donc aucun sens.
(Oui, xyzfwd.h est pénible. Je ne les utiliserais que dans les points chauds - sachant qu'il est difficile d'identifier les points chauds. Blâmez le modèle de compilation C++ + link ...)
Types de conteneurs que j'utilise habituellement lorsque la variable de conteneur est déclarée - par exemple. localement pour une variable locale, en tant que membres de classe lorsque l'instance de conteneur réelle est un membre de classe. Cela fonctionne bien si le type de conteneur réel est un détail d'implémentation, ce qui ne génère aucune dépendance supplémentaire.
S'ils font partie d'une interface particulière, ils sont déclarés avec l'interface avec laquelle ils sont utilisés, par ex.
// FrobozMangler.h
#include "Froboz.h"
typedef std::map<int, Froboz> tFrobozMap;
void Mangle(tFrobozMap const & frobozzes);
Cela devient problématique lorsque le type est un élément de liaison entre différentes interfaces - c’est-à-dire que le même type est requis par plusieurs en-têtes. Quelques solutions:
Je conviens que les deux derniers ne sont pas si géniaux, je ne les utiliserais que lorsque je serai en difficulté (pas de manière proactive).
typedef est utile dans beaucoup de situations.
En gros, cela vous permet de créer un alias pour un type. Quand/si vous devez changer le type, le reste du code pourrait rester inchangé (cela dépend du code, bien sûr) .
vector<int> v;
...
for(vector<int>::const_iterator i = v->begin(); i != v.end(); i++) {
// Stuff here
}
À l'avenir, vous voudrez peut-être modifier le vecteur avec une liste, en raison du type d'opérations à effectuer. Sans typedef, vous devez changer TOUTES les occurrences de vecteur dans votre code . Mais si vous écrivez quelque chose comme ceci:
typedef vector<int> my_vect;
my_vect v;
...
for(my_vect::const_iterator i = v->begin(); i != v.end(); i++) {
// Stuff here
}
Maintenant, il vous suffit de changer une ligne de code (c'est-à-dire de "typedef vector<int> my_vect
" à "typedef list<int> my_vect
") et tout fonctionne.
typedef vous fait également gagner du temps lorsque vous avez des structures de données complexes qui sont très longues à écrire (et difficiles à lire)
Une bonne raison d'utiliser typedef est si le type de quelque chose peut changer. Par exemple, supposons que pour l'instant, les ints 16 bits conviennent à l'indexation de certains jeux de données car, dans un avenir prévisible, vous aurez moins de 65 535 éléments et que les contraintes d'espace sont importantes ou que vous avez besoin de bonnes performances de cache. Toutefois, si vous avez besoin d'utiliser votre programme sur un jeu de données de plus de 65 535 éléments, vous souhaitez pouvoir facilement passer à un entier plus large. Utilisez un typedef, et vous ne devez le changer qu’à un seul endroit.
typedef
permet non seulement d'avoir un alias pour les types complexes, mais vous donne un endroit naturel pour documenter un type. Je l'utilise parfois à des fins documentaires.
Il y a aussi des moments où j'utilise un tableau d'octets. Maintenant, un tableau d'octets peut signifier beaucoup de choses. typedef
permet de définir mon tableau d'octets comme "hash32" ou "fileContent" pour rendre mon code plus lisible.
Il y a un autre cas d'utilisation de typedef, c'est quand on veut activer un type de code indépendant du conteneur (mais pas exactement!)
Disons que vous avez cours:
Class CustomerList{
public:
//some function
private:
typedef list<Customer> CustomerContainer;
typedef CustomerContainer::iterator Cciterator;
};
Le code ci-dessus encapsule l'implémentation du conteneur interne à l'aide de typedef. Même si, à l'avenir, le conteneur de liste doit être modifié en vecteur ou en répétition, l'utilisateur de la classe CustomerList n'a pas à s'inquiéter de l'implémentation exacte du conteneur.
Par conséquent, typedef encapsule et nous aide quelque peu à écrire du code indépendant du conteneur
Utilisations réelles de typedef:
fournir des étiquettes locales pour les types, par exemple:
template<class _T> class A
{
typedef _T T;
};
template<class _T> class B
{
void doStuff( _T::T _value );
};
Chaque fois que cela rend la source plus claire ou meilleure à lire.
J'utilise type de typedef en C # pour les génériques/modèles. Un "NodeMapping" est juste préférable de lire/utiliser et comprendre beaucoup de "Dictionnaire <string, XmlNode>". A MON HUMBLE AVIS. Donc, je le recommande pour les modèles.
Typedef permet la flexibilité dans votre classe. Lorsque vous souhaitez modifier le type de données dans le programme, vous n'avez pas besoin de modifier plusieurs emplacements, mais simplement de modifier une occurrence.
typedef <datatype example int or double> value_type
vous pouvez donner nay name au lieu de value_type
, mais value_type
est normalement le nom standard.
Donc, vous pouvez utiliser typedef comme
value_type i=0; //same as a int or double i=0;