Quand dois-je utiliser typedef en C ++?

Pendant mes années de programmation en C ++ (MFC), je n’ai jamais ressenti le besoin d’utiliser typedef , donc je ne sais pas vraiment à quoi ça sert. Où devrais-je l’utiliser? Existe-t-il des situations réelles où l’utilisation du typedef est préférée? Ou est-ce vraiment plus un mot-clé C-spécifique?

Métaprogrammation des modèles

typedef est nécessaire pour de nombreuses tâches de métaprogrammation de modèles – chaque fois qu’une classe est traitée comme une “fonction de type à la compilation”, un typedef est utilisé comme “valeur de type à la compilation” pour obtenir le type résultant. Par exemple, considérez une métafonction simple pour convertir un type de pointeur en son type de base:

 template struct ssortingp_pointer_from; template struct ssortingp_pointer_from { // Partial specialisation for pointer types typedef T type; }; 

Exemple: l’expression de type ssortingp_pointer_from::type évaluée à double . Notez que la métaprogrammation de modèles n’est généralement pas utilisée en dehors du développement de bibliothèques.

Simplifier les types de pointeur de fonction

typedef est utile pour donner un alias court et précis aux types de pointeurs de fonctions compliqués:

 typedef int (*my_callback_function_type)(int, double, std::ssortingng); void RegisterCallback(my_callback_function_type fn) { ... } 

Dans le livre de Bjarne, il déclare que vous pouvez utiliser typedef pour traiter les problèmes de portabilité entre systèmes ayant des tailles entières différentes. (ceci 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 type

 typedef long int32; 

et votre programme continuera à travailler sur la nouvelle implémentation.

utiliser avec le 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 vers une fonction renvoyant un vide et prenant un pointeur sur une autre fonction qui renvoie un vide 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. D’abord, déclarez un typedef pour “pointeur sur une fonction renvoyant un vide et ne prenant aucun argument” comme suit:

  typedef void (*pfv)(); 

Ensuite, déclarez un autre typedef pour “pointeur vers une fonction renvoyant void et en prenant une pfv” basé sur le typedef que nous avons déclaré précédemment:

  typedef void (*pf_taking_pfv) (pfv); 

Maintenant que nous avons créé le typedef pf_taking_pfv comme synonyme du “pointeur trop lourd vers une fonction renvoyant vide et prenant une pfv”, déclarer un tableau de 10 de ces pointeurs est un jeu d’enfant:

  pf_taking_pfv p[10]; 

de

Juste pour fournir quelques exemples pour les choses dites: les conteneurs STL.

  typedef std::map tFrobozMap; tFrobozMap frobozzes; ... for(tFrobozMap::iterator it=frobozzes.begin(); it!=map.end(); ++it) { ... } 

Il n’est pas rare d’utiliser 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 FrobozPtr; 

[mise à jour] Selon le commentaire – où les mettre?

Le dernier exemple – à l’aide de shared_ptr – est simple: contient du matériel 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 qu’il est sûr de l’utiliser avec une déclaration forward.

En d’autres termes: s’il existe un shared_ptr, vous ne devriez probablement utiliser le type que par l’intermédiaire de shared_ptr; séparer les déclarations n’a donc pas beaucoup de sens.

(Oui, xyzfwd.h est difficile. Je les utiliserais uniquement dans les zones sensibles – sachant que les zones sensibles sont difficiles à identifier. Blâme le modèle de compilation + lien C ++ …)

Typedefs de conteneur que j’utilise habituellement où la variable de conteneur est déclarée – par exemple, localement pour une variable locale, en tant que membre 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, ne provoquant 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 exemple

 // FrobozMangler.h #include "Froboz.h" typedef std::map 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 requirejs par plusieurs en-têtes. Quelques solutions:

  • le déclarer avec le type contenu (adapté aux conteneurs fréquemment utilisés pour ce type)
  • déplacez-les dans un en-tête séparé
  • déplacer vers un en-tête séparé et en faire une classe de données où le conteneur réel est à nouveau un détail d’implémentation

Je suis d’accord que les deux derniers ne sont pas très bons, je les utiliserais seulement quand j’ai des ennuis (pas de manière proactive).

typedef est utile dans beaucoup de situations.

Fondamentalement, il vous permet de créer un alias pour un type. Si / si vous devez changer le type, le rest du code peut restr inchangé (cela dépend du code, bien sûr). Par exemple, disons que vous voulez lancer sur un vecteur c ++

 vector v; ... for(vector::const_iterator i = v->begin(); i != v.end(); i++) { // Stuff here } 

À l’avenir, vous pouvez penser à changer le vecteur avec une liste, car le type d’opérations que vous devez effectuer à ce sujet. Sans typedef, vous devez modifier TOUTES les occurrences du vecteur dans votre code. Mais si vous écrivez quelque chose comme ceci:

 typedef vector my_vect; my_vect v; ... for(my_vect::const_iterator i = v->begin(); i != v.end(); i++) { // Stuff here } 

Il ne vous rest plus qu’à changer une ligne de code (ex: de ” typedef vector my_vect ” en ” typedef list 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 que le type de quelque chose puisse changer. Par exemple, supposons que pour l’instant, les ints 16 bits conviennent parfaitement à l’indexation de certains jeux de données car, dans un avenir prévisible, vous aurez moins de 65535 éléments et que les contraintes d’espace sont importantes. Toutefois, si vous devez utiliser votre programme sur un jeu de données contenant plus de 65535 éléments, vous souhaitez pouvoir basculer facilement sur un entier plus large. Utilisez un typedef, et vous n’avez qu’à le changer au même endroit.

typedef permet non seulement d’avoir un alias pour les types complexes, mais vous donne également une place naturelle pour documenter un type. Je l’utilise parfois à des fins de documentation.

Il y a aussi des moments où j’utilise un tableau d’octets. Maintenant, un tableau d’octets pourrait signifier beaucoup de choses. typedef permet de définir mon tableau d’octets comme “hash32” ou “fileContent” pour rendre mon code plus lisible.

Utilisations réelles du typedef:

  • fournir des alias amicaux pour les types modélisés à long terme
  • fournissant des alias conviviaux pour les types de pointeurs de fonctions
  • fournir des étiquettes locales pour les types, par exemple:

     template class A { typedef _T T; }; template class B { void doStuff( _T::T _value ); }; 

Il y a un autre cas d’utilisation pour utiliser typedef, c’est quand nous voulons activer une sorte de code indépendant du conteneur (mais pas exactement!)

Disons que vous avez de la classe:

 Class CustomerList{ public: //some function private: typedef list CustomerContainer; typedef CustomerContainer::iterator Cciterator; }; 

Le code ci-dessus encapsule l’implémentation du conteneur interne à l’aide de typedef et même si à l’avenir le conteneur de liste doit être remplacé par vector ou deque encore, l’utilisateur de la classe CustomerList n’a pas à se soucier de l’implémentation exacte du conteneur.

Par conséquent, le typedef encapsule et nous aide quelque peu à écrire du code indépendant du conteneur.

Chaque fois que la source devient plus claire ou meilleure à lire.

J’utilise une sorte de typedef en C # pour les génériques / modèles. Un “NodeMapping” est juste préférable de lire / utiliser et comprendre alors beaucoup de “Dictionary “. A MON HUMBLE AVIS. Je le recommande donc pour les modèles.

Typedef permet une 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 vous devez simplement modifier une occurrence.

 typedef  value_type 

vous pouvez donner un nom non à la place à 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; 

… et vous n’avez pas besoin d’un typedef pour une énumération ou une structure.

Ou vous?

 typedef enum { c1, c2 } tMyEnum; typedef struct { int i; double d; } tMyStruct; 

peut être mieux écrit comme

 enum tMyEnum { c1, c2 } struct tMyStruct { int i; double d; }; 

Est-ce exact? Qu’en est-il de C?