J'ai parcouru du code et remarqué que la convention consistait à transformer des types de pointeur comme
SomeStruct*
dans
typedef SomeStruct* pSomeStruct;
Y at-il un mérite à cela?
Cela peut être approprié lorsque le pointeur lui-même peut être considéré comme une "boîte noire", c'est-à-dire une donnée dont la représentation interne doit être sans rapport avec le code.
Essentiellement, si votre code va jamais déréférencer le pointeur et que vous le transmettez simplement aux fonctions de l'API (parfois par référence), non seulement le typedef réduit-il le nombre de *
s dans votre code, mais il suggère également au programmeur que le pointeur ne devrait pas vraiment être manipulé.
Cela facilite également le changement ultérieur de l'API si le besoin s'en fait sentir. Par exemple, si vous utilisez un ID plutôt qu'un pointeur (ou inversement), le code existant ne sera pas rompu car le pointeur n'a jamais été supposé être déréférencé.
Pas dans mon expérience. Cacher le '*
' rend le code difficile à lire.
La seule fois où j'utilise un pointeur à l'intérieur de la typedef, c'est lorsqu'il s'agit de pointeurs sur des fonctions:
typedef void (*SigCatcher(int, void (*)(int)))(int);
typedef void (*SigCatcher)(int);
SigCatcher old = signal(SIGINT, SIG_IGN);
Sinon, je les trouve plus déroutant qu'utile.
signal()
, pas sur le capteur de signal. Il pourrait être clarifié (en utilisant le type SigCatcher
corrigé ci-dessus) en écrivant: typedef SigCatcher (*SignalFunction)(int, SigCatcher);
Ou, pour déclarer la fonction signal()
:
extern SigCatcher signal(int, SigCatcher);
Autrement dit, une SignalFunction
est un pointeur sur une fonction qui prend deux arguments (un int
et un SigCatcher
) et retourne un SigCatcher
. Et signal()
lui-même est une fonction qui prend deux arguments (un int
et un SigCatcher
) et renvoie un SigCatcher
.
Cela peut vous aider à éviter certaines erreurs. Par exemple dans le code suivant:
int* pointer1, pointer2;
pointer2 n'est pas un int * , c'est simple int . Mais avec typedefs cela n'arrivera pas:
typedef int* pInt;
pInt pointer1, pointer2;
Ils sont tous les deux int * maintenant.
Ma réponse est un "non" clair.
Pourquoi?
Tout d’abord, vous échangez simplement un seul caractère *
contre un autre caractère unique p
. C'est zéro gain. Cela seul devrait vous empêcher de le faire car il est toujours mauvais de faire des choses supplémentaires inutiles.
Deuxièmement, et c’est la raison importante, the *
a une signification qu’il n’est pas bon de cacher. Si je passe quelque chose à une fonction comme celle-ci
void foo(SomeType bar);
void baz() {
SomeType myBar = getSomeType();
foo(myBar);
}
Je ne m'attends pas à ce que la signification de myBar
soit modifiée en passant à foo()
. Après tout, je passe par valeur, donc foo()
ne voit jamais une copie de myBar
non? Pas lorsque SomeType
est aliasé pour signifier une sorte de pointeur!
Cela s'applique aussi bien aux pointeurs C qu'aux pointeurs intelligents C++: si vous masquez le fait qu'ils constituent des pointeurs vers vos utilisateurs, vous créerez une confusion totalement inutile. Alors, s'il vous plaît, ne pas alias vos pointeurs.
(Je crois que l'habitude de taper des types de pointeurs est simplement une tentative malavisée de cacher combien d'étoiles on a en tant que programmeur http://wiki.c2.com/?ThreeStarProgrammer .)
Cela (comme tant de réponses) dépend.
En C, cela est très courant car vous essayez de dissimuler qu'un objet est un pointeur. Vous essayez de laisser entendre que c’est l’objet que toutes vos fonctions manipulent (nous savons que c’est un pointeur situé en dessous, mais il représente l’objet que vous manipulez).
MYDB db = MYDBcreateDB("Plop://djdjdjjdjd");
MYDBDoSomthingWithDB(db,5,6,7);
CallLocalFuc(db); // if db is not a pointer things could be complicated.
MYDBdestroyDB(db);
MYDB se trouve sous un pointeur sur un objet.
En C++, cela n'est plus nécessaire.
Principalement parce que nous pouvons passer des choses par référence et que les méthodes sont intégrées à la déclaration de classe.
MyDB db("Plop://djdjdjjdjd");
db.DoSomthingWithDB(5,6,7);
CallLocalFuc(db); // This time we can call be reference.
db.destroyDB(); // Or let the destructor handle it.
C'est une question de style. Vous voyez ce type de code très fréquemment dans les fichiers d’en-tête de Windows. Bien qu'ils aient tendance à préférer la version tout en majuscule au lieu de préfixer par une minuscule p.
Personnellement, j'évite cette utilisation de typedef. Il est beaucoup plus clair que l'utilisateur dise explicitement qu'il veut un Foo * plutôt que PFoo. Les typedef sont les mieux adaptés ces jours-ci pour rendre lisible STL :)
typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
Typedef est utilisé pour rendre le code plus lisible, mais rendre le pointeur comme typedef augmentera la confusion. Mieux vaut éviter les pointeurs typés.
Si vous faites cela, vous ne pourrez pas créer de conteneurs STL de const pSomeStruct puisque le compilateur lit:
list<const pSomeStruct> structs;
comme
list<SomeStruct * const> structs;
ce qui n’est pas un conteneur STL légal puisque les éléments ne sont pas assignables.
Voir cette question .
Win32 API fait cela avec à peu près toutes les structures (sinon toutes)
POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2
C'est gentil de la façon dont il est cohérent, mais à mon avis cela n'ajoute pas d'élégance.
Il y a quelque temps, j'aurais répondu "non" à cette question. Maintenant, avec la montée des pointeurs intelligents, les pointeurs ne sont plus toujours définis avec une étoile '*'. Il n’ya donc rien d’évident à ce que le type soit un pointeur ou non.
Alors maintenant, je dirais: les pointeurs typés sont acceptables, à condition qu'il soit clairement indiqué qu'il s'agit d'un "type de pointeur". Cela signifie que vous devez utiliser un préfixe/suffixe spécifiquement pour cela. Non, "p" n'est pas un préfixe suffisant, par exemple. J'irais probablement avec "ptr".
Le but avec typedef est de cacher les détails de l'implémentation, mais typedef-ing la propriété pointer cache trop et rend le code plus difficile à lire/à comprendre .
Si vous souhaitez masquer les détails d'implémentation (ce qui est souvent une bonne chose à faire), ne masquez pas la partie pointeur. Prenons l'exemple du prototype de l'interface FILE
standard:
FILE *fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, FILE *stream);
ici, fopen renvoie un pointeur à une structure FILE
(pour laquelle vous ne connaissez pas les détails de la mise en oeuvre). FILE
n'est peut-être pas un si bon exemple, car dans ce cas, cela aurait pu fonctionner avec un type de fichier pFILE masquant le fait qu'il s'agisse d'un pointeur.
pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);
Cependant, cela ne fonctionnerait que parce que vous ne jouez jamais avec le contenu qui est indiqué directement. Le moment où vous tapez un pointeur indiquant que certains endroits modifient le code devient très difficile à lire dans mon expérience.
Non.
Cela rendra votre vie misérable au moment où vous la mélangez avec const
typedef foo *fooptr;
const fooptr bar1;
const foo *bar2
bar1
et bar2
sont-ils du même type?
Et oui, je ne fais que citer le gourou de Herb Sutter. Elle a beaucoup parlé de vérité. ;)
-- Modifier --
Ajout de lien à l'article cité.
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835