Это хорошая идея, чтобы печатать указатели? - PullRequest
68 голосов
/ 15 апреля 2009

Я просмотрел некоторый код и заметил, что условием было переключение типов указателей, например

SomeStruct* 

в

typedef SomeStruct* pSomeStruct;

Есть ли в этом какая-то заслуга?

Ответы [ 14 ]

96 голосов
/ 15 апреля 2009

Это может быть уместно, когда сам указатель можно рассматривать как «черный ящик», то есть фрагмент данных, внутреннее представление которого не должно иметь отношения к коду.

По сути, если ваш код никогда не разыменует указатель, и вы просто передаете его функциям API (иногда по ссылке), тогда typedef не только уменьшает количество * s в вашем код, но также предлагает программисту, что указатель не должен быть на самом деле вмешиваться.

Это также облегчает изменение API в будущем, если возникнет такая необходимость. Например, если вы переключитесь на использование идентификатора, а не указателя (или наоборот), существующий код не будет поврежден, потому что указатель никогда не должен был разыменовываться.

69 голосов
/ 15 апреля 2009

Не в моем опыте. Скрытие '*' затрудняет чтение кода.

28 голосов
/ 15 апреля 2009

Единственный раз, когда я использую указатель внутри typedef, это когда работаю с указателями на функции:

typedef void (*SigCatcher(int, void (*)(int)))(int);

typedef void (*SigCatcher)(int);

SigCatcher old = signal(SIGINT, SIG_IGN);

В противном случае я нахожу их более запутанными, чем полезными.


Вычеркнутое объявление является правильным типом для указателя на функцию signal(), а не для ловушки сигнала. Это можно сделать более понятным (используя исправленный тип SigCatcher выше), написав: typedef SigCatcher (*SignalFunction)(int, SigCatcher); Или, чтобы объявить функцию signal(): extern SigCatcher signal(int, SigCatcher); То есть SignalFunction - это указатель на функцию, которая принимает два аргумента (int и SigCatcher) и возвращает SigCatcher. И signal() сама по себе является функцией, которая принимает два аргумента (int и SigCatcher) и возвращает SigCatcher.
14 голосов
/ 15 апреля 2009

Это может помочь вам избежать некоторых ошибок. Например, в следующем коде:

int* pointer1, pointer2;

pointer2 не является int *, это просто int . Но с typedefs этого не произойдет:

typedef int* pInt;
pInt pointer1, pointer2;

Они оба int * сейчас.

11 голосов
/ 10 апреля 2017

Мой ответ - «Нет».

Почему?

Ну, во-первых, вы просто меняете один символ * на другой символ p. Это ноль усиление. Одно это должно удерживать вас от этого, так как всегда плохо делать лишние бессмысленные вещи.

Во-вторых, и это важная причина, * несет значение, которое нехорошо прятать . Если я передам что-то в функцию, подобную этой

void foo(SomeType bar);

void baz() {
    SomeType myBar = getSomeType();
    foo(myBar);
}

Я не ожидаю, что значение myBar будет изменено путем передачи его в foo(). В конце концов, я передаю по значению, поэтому foo() только когда-либо видит копию myBar верно? Не тогда, когда псевдоним SomeType означает какой-то указатель!

Это относится как к указателям на C, так и к интеллектуальным указателям на C ++: если вы скрываете тот факт, что они являются указателями для ваших пользователей, вы создадите путаницу, которая совершенно не нужна. Поэтому, пожалуйста, не используйте псевдонимы.

(Я считаю, что привычка определять типы указателей - просто ошибочная попытка скрыть, сколько звезд у программиста http://wiki.c2.com/?ThreeStarProgrammer.)

5 голосов
/ 15 апреля 2009

Это (как и многие ответы) зависит.

В C это очень распространено, так как вы пытаетесь скрыть, что объект является указателем. Вы пытаетесь понять, что это объект, которым манипулируют все ваши функции (мы знаем, что это указатель внизу, но он представляет объект, которым вы манипулируете).

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, вероятно, находится указатель на какой-то объект.

В C ++ это больше не требуется.
Главным образом потому, что мы можем передавать вещи по ссылке, а методы включаются в объявление класса.

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 голосов
/ 15 апреля 2009

Это вопрос стиля. Этот вид кода очень часто встречается в заголовочных файлах Windows. Хотя они, как правило, предпочитают все заглавные буквы версии вместо префикса строчными буквами p.

Лично я избегаю такого использования typedef. Гораздо понятнее, если пользователь явно скажет, что хочет Foo *, чем PFoo. Typedef лучше всего подходят для чтения STL :))

typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
4 голосов
/ 09 апреля 2017

Обсуждение подготовлено в предположении, что интересующим языком является C. Разветвления для C ++ не рассматривались.

Использование указателя typedef для структуры без тегов

Вопрос Размер структуры, определяемой как указатель , вызывает интересную информацию об использовании typedef для (структуры) указателей.

Рассмотрим определение типа бетонной (не непрозрачной) конструкции без тега:

typedef struct { int field1; double field2; } *Information;

Подробности членов абсолютно касаются этого обсуждения; все, что имеет значение, - то, что это не непрозрачный тип, такой как typedef struct tag *tag; (и вы не можете определить такие непрозрачные типы через typedef без тега).

Возникает вопрос: «Как узнать размер этой структуры?»

Краткий ответ: «только через переменную типа». Нет тега для использования с sizeof(struct tag). Например, вы не можете писать sizeof(*Information), а sizeof(Information *) - это размер указателя на тип указателя, а не размер типа структуры.

Фактически, если вы хотите выделить такую ​​структуру, вы не можете создать ее, за исключением динамического выделения (или суррогатных методов, которые имитируют динамическое распределение). Нет никакого способа создать локальную переменную типа структуры, указатели которой называются Information, а также нет способа создать переменную области видимости файла (глобальную или static) типа структуры, а также нет способа внедрить такую ​​структуру (в отличие от указателя на такую ​​структуру) в другую структуру или тип объединения.

Вы можете - должны - написать:

Information info = malloc(sizeof(*info));

Помимо того, что указатель скрыт в typedef, это хорошая практика - если тип info изменится, распределение по размеру останется точным. Но в этом случае это также единственный способ получить размер структуры и выделить структуру. И нет другого способа создать экземпляр структуры.

Это вредно?

Это зависит от ваших целей.

Это не непрозрачный тип - детали структуры должны быть определены, когда тип указателя typedef 'd.

Это тип, который может использоваться только с динамическим распределением памяти.

Это тип безымянный. Указатель на тип структуры имеет имя, а сам тип структуры - нет.

Если вы хотите задействовать динамическое распределение, похоже, это способ сделать это.

В целом, однако, это скорее вызывает замешательство и беспокойство, чем просветление.

Резюме

Как правило, плохая идея использовать typedef для определения указателя на тип структуры без тега.

4 голосов
/ 15 апреля 2009

Typedef используется для того, чтобы сделать код более читабельным, но указатель типа typedef увеличит путаницу. Лучше избегать указателей typedef.

3 голосов
/ 27 января 2010

Если вы сделаете это, вы не сможете создавать STL-контейнеры const pSomeStruct, так как компилятор читает:

list<const pSomeStruct> structs;

в

list<SomeStruct * const> structs;

, который не является допустимым контейнером STL, поскольку элементы не могут быть назначены.

См. вопрос .

...