Это хорошая идея для указателей typedef?
Я просмотрел какой-то код и заметил, что соглашение состоит в том, чтобы включить типы указателей, например
SomeStruct*
в
typedef SomeStruct* pSomeStruct;
Есть ли в этом заслуга?
Ответы
Ответ 1
Это может быть уместно, когда сам указатель можно рассматривать как "черный ящик", то есть часть данных, внутреннее представление которых не должно иметь отношения к коду.
По сути, если ваш код никогда не разыменует указатель, и вы просто передаете его функциям API (иногда по ссылке), тогда typedef не только уменьшает количество *
в вашем коде, но также предлагает программисту, что указатель не должен на самом деле не вмешиваться.
Это также облегчает изменение API в будущем, если возникнет такая необходимость. Например, если вы переключитесь на использование идентификатора, а не указателя (или наоборот), существующий код не будет поврежден, потому что указатель никогда не должен был разыменовываться с самого начала.
Ответ 2
Не в моем опыте. Скрытие "*
" делает код трудным для чтения.
Ответ 3
Единственный раз, когда я использую указатель внутри 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);
То есть a SignalFunction
является указателем на функцию, которая принимает два аргумента (a int
и a SigCatcher
) и возвращает a SigCatcher
. А сама signal()
- это функция, которая принимает два аргумента (a int
и a SigCatcher
) и возвращает a SigCatcher
.
Ответ 4
Это поможет избежать некоторых ошибок. Например, в следующем коде:
int* pointer1, pointer2;
pointer2 не является int *, он просто int.
Но с typedefs это не произойдет:
typedef int* pInt;
pInt pointer1, pointer2;
Теперь они int *.
Ответ 5
Мой ответ - "Нет".
Зачем?
Ну, во-первых, вы просто меняете один символ *
на другой символ p
. Это нулевое усиление. Одно это должно удерживать вас от этого, так как всегда плохо делать лишние бессмысленные вещи.
Во-вторых, и это важная причина, *
несет значение, которое нехорошо скрывать. Если я передам что-то в функцию, как это
void foo(SomeType bar);
void baz() {
SomeType myBar = getSomeType();
foo(myBar);
}
Я не ожидаю, что значение myBar
изменится, передав его в foo()
. В конце концов, я myBar
по значению, так что foo()
только когда-либо видит копию myBar
верно? Не тогда, когда SomeType
имеет псевдоним, чтобы обозначать какой-то указатель!
Это относится как к указателям на C, так и к интеллектуальным указателям C++: если вы скрываете тот факт, что они являются указателями для ваших пользователей, вы создадите путаницу, которая совершенно не нужна. Поэтому, пожалуйста, не используйте псевдонимы.
(Я считаю, что привычка определять типы указателей - просто ошибочная попытка скрыть, сколько звезд у программиста http://wiki.c2.com/?ThreeStarProgrammer.)
Ответ 6
Это вопрос стиля. Этот код очень часто встречается в заголовочных файлах Windows. Хотя они, как правило, предпочитают всю версию верхнего регистра вместо префикса с нижним регистром p.
Лично я избегаю использования typedef. Гораздо понятнее, что пользователь явно говорит, что хочет Foo *, чем PFoo. Typedef лучше всего подходят для чтения STL:)
typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
Ответ 7
Он (как и многие ответы) зависит.
В 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, вероятно, указатель на некоторый объект.
В С++ это больше не требуется.
Главным образом потому, что мы можем передавать информацию по ссылке и методы включены в объявление класса.
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.
Ответ 8
Typedef используется, чтобы сделать код более читаемым, но создание указателя в качестве typedef приведет к увеличению путаницы. Лучше избегать указателей typedef.
Ответ 9
Обсуждение поставлено в предположении, что язык, представляющий интерес, является 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
для определения указателя на тип тегов без тегов.
Ответ 10
Если вы сделаете это, вы не сможете создавать контейнеры STL для const pSomeStruct, поскольку компилятор читает:
list<const pSomeStruct> structs;
а
list<SomeStruct * const> structs;
который не является законным контейнером STL, поскольку элементы не могут быть назначены.
См. этот вопрос .
Ответ 11
Номер
Это сделает вашу жизнь несчастной, как только вы смешаете ее с const
typedef foo *fooptr;
const fooptr bar1;
const foo *bar2
Являются ли bar1
и bar2
одним и тем же типом?
И да, я просто цитирую Герба Саттера. Много правды она говорила. ;)
- Изменить -
Добавление ссылки на цитируемую статью.
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
Ответ 12
Win32 API делает это примерно с каждой структурой (если не все)
POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2
Неплохо, как это согласовано, но, на мой взгляд, оно не добавляет никакой элегантности.
Ответ 13
Цель с typedef заключается в том, чтобы скрыть детали реализации, но типизация свойства указателя слишком много скрывает и делает код более трудным для чтения/понимания.
Поэтому, пожалуйста, не делайте этого.
Если вы хотите скрыть детали реализации (что часто бывает полезно), не скрывайте часть указателя. Возьмем, к примеру, прототип стандартного интерфейса FILE
:
FILE *fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, FILE *stream);
здесь fopen возвращает указатель на некоторую структуру FILE
(о которой вы не знаете детали реализации). Возможно, FILE
не такой хороший пример, потому что в этом случае он мог бы работать с некоторым типом pFILE, который скрывал тот факт, что это указатель.
pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);
Однако это будет работать только потому, что вы никогда не обходитесь с контентом, на который указывают напрямую. В тот момент, когда вы напечатали какой-то указатель на то, что некоторые места меняют код, очень трудно прочитать в моем опыте.
Ответ 14
Некоторое время назад я ответил бы "нет" на этот вопрос. Теперь, с появлением умных указателей, указатели не всегда определяются звездочкой '*'. Таким образом, нет ничего очевидного в том, что тип является указателем или нет.
Итак, теперь я бы сказал: это хорошо для указателей typedef, если будет ясно, что это "тип указателя". Это означает, что вам нужно использовать префикс/суффикс специально для него. Нет, например, "p" не является достаточным префиксом. Я бы, наверное, пошел с "ptr".