web-dev-qa-db-fra.com

Est-ce une bonne idée de dactylographier des pointeurs?

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?

66
Unknown

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é.

94
Artelius

Pas dans mon expérience. Cacher le '*' rend le code difficile à lire.

67
sigjuice

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.


La déclaration rayée correspond au type correct pour un pointeur sur la fonction 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.

28
Jonathan Leffler

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.

16
Sad Developer

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 .)

9
cmaster

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.
5
Martin York

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;
5
JaredPar

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.

4
Chand

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 .

3
Dan Hook

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.

2
dreamlax

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".

1
Benoît

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.

0
hlovdal

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

0
dgnuff