Это хорошая идея, чтобы печатать указатели?

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

SomeStruct* 

в

typedef SomeStruct* pSomeStruct;

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

15 ответов

Решение

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

По сути, если ваш код никогда не разыменует указатель, и вы просто передаете его функциям 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);

Это SignalFunction указатель на функцию, которая принимает два аргумента (int и SigCatcher) и возвращает SigCatcher, А также signal() сама по себе является функцией, которая принимает два аргумента (int и SigCatcher) и возвращает SigCatcher,

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

int* pointer1, pointer2;

Указатель2 не является int *, это просто int. Но с typedefs этого не произойдет:

typedef int* pInt;
pInt pointer1, pointer2;

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

Мой ответ - "Нет".

Зачем?

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

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

void foo(SomeType bar);

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

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

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

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

Обсуждение было принято, предполагая, что интересующий язык - 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 определить указатель на тип структуры без тега.

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

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

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

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

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

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.

Нет.

Это сделает вашу жизнь несчастной, как только вы смешаете это с const

typedef foo *fooptr;
const fooptr bar1;
const foo *bar2

Являются bar1 а также bar2 того же типа?

И да, я просто цитирую Гуру Херба Саттера. Много правды она говорила.;)

-- Редактировать --

Добавление ссылки на цитируемую статью.

http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835

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

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

list<const pSomeStruct> structs;

как

list<SomeStruct * const> structs;

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

Смотрите этот вопрос.

В общем, нет. В конкретных случаях да.

Есть пара конструкций, на которые ссылаются некоторые другие ответы, и это типы только для указателей . На ум приходит пара конструкций типа «только указатель». Если кто придумает еще, добавлю в список.

Непрозрачные типы

Это типы, реализация которых полностью скрыта от пользователя. Обычно в заголовке вы видите структуру typedef, но не реализацию этой структуры. Из-за этого вы не можете разыменовывать значения этих типов. Все функции, работающие с этим типом, принимают указатели на эти типы. В таких ситуациях уместно добавить указатель на typedef. Вы часто видите эти типы, называемые "дескрипторами".

      typedef struct handle_ * handle;

handle get_handle(string s);
void mutate_handle(handle h, value v);
void release_handle(handle h);

Гибкие типы элементов массива

Другим типом, использующим только указатели, являются типы, определенные с элементами гибкого массива (FAM). Последний элемент в типе FAM является неограниченным массивом. Вы должны динамически выделять память для этих типов, а гибкий массив рассматривается как встроенный в структуру. Вы можете получить доступ к полям в типе FAM, но не можете разыменовать весь объект. Здесь также уместно добавить указатель на typedef.

      typedef struct string {
    size_t capacity;
    size_t length;
    char buffer[];
} * string;

string string_new(void);
size_t string_length(string s);
void string_append(string * s, char c);
void string_delete(string * s);

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

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

Приятно, как это соответствует, но по моему мнению это не добавляет элегантности.

Цель 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);

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

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

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

Другие вопросы по тегам