2010-09-23 2 views
14

Можно создать дубликат:
Typedef pointers a good idea?Является ли typedef'ing указательным типом считается плохой практикой?

Я видел эту странность во многих API, я использовал:

typedef type_t *TYPE; 

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

Вы, как и я, думаете, что это вызывает много путаницы? Это значит обеспечить инкапсуляцию, или есть другие причины? Считаете ли вы, что это плохая практика?

+0

Confusions Confusions! –

+1

Только один из ответов «Указатели Typedef - хорошая идея?» ссылается на бесполезное взаимодействие с 'const' - и этот ответ с низким рейтингом. ИМХО проблема 'const' является самой значительной проблемой с этими типами typedefs ... следовательно, не является действительно дубликатом. –

+0

@John: Я не согласен с вами заключение. Вы отметили, что ответ, добавленный после того, как задан вопрос, не получил большого внимания, но вопросы по-прежнему принципиально одинаковы. – dmckee

ответ

22

В целом, это плохая практика.Существенная проблема заключается в том, что она не очень хорошо играть с const:

typedef type_t *TYPE; 
extern void set_type(TYPE t); 

void foo(const TYPE mytype) { 
    set_type(mytype); // Error expected, but in fact compiles 
} 

Для того, чтобы автор foo(), чтобы выразить то, что они на самом деле означает, библиотека, которая предоставляет TYPE также должны обеспечить CONST_TYPE:

typedef const type_t *CONST_TYPE; 

, так что foo() может иметь подпись void foo(CONST_TYPE mytype), и в этот момент мы спустились в фарс.

Отсюда правило:

сделать из определений типов структур (в частности, неполные структуры), а не указатели на эти структуры.

Если определение базовой структуры не должна быть общедоступными (который часто похвально), то, что инкапсуляция должна быть поставлена ​​структурами является неполным, а не неудобными определениями типов:

struct type_t; 
typedef struct type_t type_t; 

void set_type(type_t *); 
int get_type_field(const type_t *); 
+0

Интересно, что некоторым типам не нужна версия const. Например, я никогда не чувствовал никакого желания для 'const_pthread_t', на котором я могу назвать' pthread_getschedparam', но не 'setchedparam'. 'const pthread_t' не достигает этого (конечно), точно так же, как указатель typedefed не может его достичь в вашем примере. И 'ftell' принимает' FILE * ', а не' const FILE * ', хотя я полагаю, что он должен быть const, чтобы поддерживать постоянное использование FILE *. Поэтому я думаю, что эта проблема 'const' является существенной/решающей в некоторых случаях, но не всегда. Может быть, предсказательное предсказание C может показаться? Или просто лень :-) –

+0

Действительно. Я посмотрел, не было ли что-либо в стандартном варианте с 'const FILE *', но ... не так много. (И я надеялся помочь вам понять 'ФАЙЛ *'! :-)) Возможно, это связано с тем, что-то похоже на атомный тип - не так много написано 'const int'. Или что-то вроде проблемы с курицей и яйцом: если ни одна из функций запроса не принимает 'const FILE *', почему бы вам написать свою собственную функцию 'foo()', чтобы взять ее - как бы вы использовали свой параметр? –

+1

'ftell' не может принимать' const FILE * ', потому что (в POSIX или любой другой реализации с потоками) он должен получить блокировку mutex для объекта' FILE', если текущая позиция не может быть получена с помощью одной атомной операции. Вся идея «const FILE *» - это большая бессмыслица; его нельзя было использовать ни для чего. –

6

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

Иногда необходимо использовать только тип указателя, если структура, на которую он указывает, не является общедоступной. Это помогает упростить скрытие данных. То есть

typedef struct hidden_secret_object * object; 
void change_object(object foo); 

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

+2

Зачем вам нужен указатель в typedef для скрытия данных? 'typedef struct h_s_o объект; void change_object (object * foo); 'выполнит то же самое. Этот пример не имеет отношения к вопросу. – schot

+0

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

0

Может быть способ сделать его более конкретным бы назвать новый тип указателя type_ptr или что-то подобное:

typedef type_t* type_ptr; 
5

Я не нахожу это понятно тоже. Я тоже не люблю полностью капитализированные типы (я пытаюсь зарезервировать их для #defines).

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

Суффикс (как упоминалось ранее) _p, _ptr, Pointer или что-то в этом направлении создает ясность; увеличивает типизацию, это правда, но предотвратит вас от глупых ошибок (например, используя «.» вместо «->», ...), которые ценят вам ценное время разработки.

1

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

Я также не думаю, что это плохая практика, если вы делаете это, потому что у вас есть несколько уровней косвенности, и вы хотите использовать ее в ситуациях, когда некоторые из них не имеют значения. typedef char **argarray, или что-то в этом роде.

Если пользователь должен разыменовать его, то в C, я думаю, что лучше всего сохранить *. В C++ люди используются для пользовательских типов с перегруженным operator*, например, итераторами. В C это просто не нормально.

0

Квалификаторы класса хранения, такие как «const», будут работать по-разному с указателями typedef'ed, чем с «естественными». Хотя это обычно не очень хорошо для «const», это может быть очень полезно для классов хранения специфичных для компилятора, таких как «xdata». Объявление типа:

 
xdata WOKKA *foo; 

объявим «Foo», чтобы быть указатель, хранящийся в классе хранения по умолчанию, к WOKKA в XData. Объявление:

 
xdata WOKKA_PTR bar; 
объявит «бар» указателем, хранящимся в xdata, в WOKKA в любом классе хранения, указанном в WOKKA_PTR. Если библиотечные процедуры ожидают указания указателей на вещи с определенным классом хранения, может оказаться полезным определить эти классы хранения в типах указателей.

0

Это укусил меня в попу по поводу:

for (vector<typedef_name_that_doesnt_indicate_pointerness_at_all>::iterator it; 
    it != v.end(); ++it) 
{ 
    it->foo(); // should have been written (*it)->foo(); 
} 

Единственный раз, когда это приемлемо, если тип предназначается, чтобы быть по-настоящему непрозрачной и не обращались непосредственно на всех. IOW, если кто-то захочет разыграть его за пределами API, тогда pointerness не следует скрывать за typedef.

4

Это зависит от того, чего вы пытаетесь достичь. Нет никакого значимого ответа «да» или «нет» на ваш вопрос, как это указано.

  • Если вы пытаетесь создать абстрактные ручки вида типа, что означает, что пользователь не должен знать, или все равно, что скрывается за типом, то ЬурейеЙ-кий типа указателя прекрасно. Все дело в том, что сегодня это может быть тип указателя, а завтра он может стать целым типом, а позже он может стать чем-то другим. Это именно то, что тип typedef типа указателя обычно используется для большинства интерфейсов библиотеки.

Вы говорите, что иногда «не ясно, что указатель объявлен». Но при этой модели использования это точно так! Это предположительно быть «непонятным». Тот факт, что тип оказывается запутанным указателем, не является вашим бизнесом. Это то, что вам не нужно знать и не полагаться на него.

Классическим примером этой модели использования является тип va_list в стандартной библиотеке. В некоторой реализации это может легко быть typedef для типа указателя. Но это то, о чем вы не должны знать или полагаться.

Другим примером может служить определение типа HWND в Windows API. Это тип typedef для типа указателя, но это не ваш бизнес.

  • Совсем иная ситуация, когда вы ЬурейиЙ-ки типа указателя как форма стенографии, просто чтобы сделать заявления короче, не имея необходимости печатать * персонажу каждый раз. В этом случае пользователю предоставляется пользовательский тип typedef (и всегда будет), стоящий за типом указателя. Обычно это использование не является хорошей практикой программирования. Если пользователи захотят создать псевдоним, чтобы избежать ввода * каждый раз, они могут сделать это сами.

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

Пример этого плохого использования typedefs также можно найти в Windows API. Названия Typedef, такие как PINT, соответствуют именно этой ошибочной модели использования.

+0

+1 не мог бы сказать это лучше –

Смежные вопросы