Это хорошая идея для указателей typedef?

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

SomeStruct* 

на

typedef SomeStruct* pSomeStruct;

есть ли заслуга в этом?

13 ответов


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

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

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


не по моему опыту. Прятать '* ' затрудняет чтение кода.


единственный раз, когда я использую указатель внутри 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 указатель на функцию, которая принимает два аргумента (один int и SigCatcher) и возвращает SigCatcher. И signal() сама по себе является функцией, которая принимает два аргумента (int и SigCatcher) и возвращает SigCatcher.


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

int* pointer1, pointer2;

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

typedef int* pInt;
pInt pointer1, pointer2;

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


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

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

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

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

typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;

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


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

list<const pSomeStruct> structs;

as

list<SomeStruct * const> structs;

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

посмотреть этот вопрос .


мой ответ-однозначное "нет".

почему?

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

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

void foo(SomeType bar);

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

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

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

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


Win32 API делает это почти с каждой структурой (если не со всеми)

POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2

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


некоторое время назад, я бы ответил "Нет" на этот вопрос. Теперь, с ростом умных указателей, указатели не всегда определяются звездой '*' больше. Таким образом, нет ничего очевидного в том, что тип является указателем или нет.

Итак, теперь я бы сказал: это нормально для указателей typedef, если очень ясно, что это "тип указателя". Это означает, что вы должны использовать префикс/суффикс специально для него. Нет, например," p " не является достаточным префиксом. Я бы, наверное, пошел с "запись ptr."


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

использование указателя typedef для не помеченной структуры

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

рассмотрите tagless конкретный (непрозрачный) тип структуры определение:

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 для определения указателя на тип структуры без тегов.


цель typedef-скрыть детали реализации, но typedef - ing свойство pointer скрывает слишком много и затрудняет чтение/понимание кода. Так что, пожалуйста, не делай этого.


если вы хотите скрыть детали реализации (что часто является хорошей вещью), не скрывайте часть указателя. Возьмем, к примеру, прототип для стандарта FILE интерфейс:

FILE *fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, FILE *stream);

здесь fopen возвращает указатель в какой-то структуре FILE (для которого вы не знаете детали реализации). Может быть!--2--> не такой хороший пример, потому что в этом случае он мог бы работать с некоторым типом pFILE, который скрывал тот факт, что это указатель.

pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);

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