Я переглянув якийсь код і помітив, що умова полягає у перетворенні типів покажчиків на зразок
SomeStruct*
в
typedef SomeStruct* pSomeStruct;
Чи є в цьому якась заслуга?
Я переглянув якийсь код і помітив, що умова полягає у перетворенні типів покажчиків на зразок
SomeStruct*
в
typedef SomeStruct* pSomeStruct;
Чи є в цьому якась заслуга?
Відповіді:
Це може бути доречним, коли сам вказівник можна розглядати як "чорний ящик", тобто фрагмент даних, внутрішнє представлення якого не повинно мати значення для коду.
По суті, якщо ваш код ніколи не розмежовує покажчик, а ви просто передаєте його навколо функцій API (іноді за посиланням), тоді typedef не тільки зменшує кількість *
s у вашому коді, але й пропонує програмісту, що вказівник не повинен з вами насправді не втручаються.
Це також полегшує зміну API в майбутньому, якщо виникне необхідність. Наприклад, якщо ви перейдете до використання ідентифікатора, а не вказівника (або навпаки), існуючий код не зламається, оскільки вказівник ніколи не повинен був бути розменованим.
FILE*
не було визначено типом, оскільки непрозорий вказівник полягає в тому, щоб дозволити макроси, такі як, getc()
і putc()
безпосередньо маніпулювати структурою FILE та уникати накладних викликів функції в більшості випадків.
Не з мого досвіду. Якщо приховати ' *
', код важко читається.
Device
і ви typedef
його Device_Ptr
(замість того, щоб використовувати Device *
) або подібний, він цілком читається. Я бачу багато дозрілих і досить великих бібліотек, які роблять це. Особливо в C ++, якщо ви додаєте шаблони, це може пощадити ваші пальці.
myTypePtr
або взяв би myTypeRef
цю зірку в будь-який день. : P
Єдиний раз, коли я використовую покажчик всередині 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
- це вказівник на функцію, яка приймає два аргументи (an int
і a SigCatcher
) і повертає a SigCatcher
. А signal()
сама є функцією, яка приймає два аргументи (an int
і a SigCatcher
) і повертає a SigCatcher
.
sig_t
відповідає моєму SigCatcher
, і так, це signal()
надзвичайно спрощує декларацію .
Це може допомогти вам уникнути деяких помилок. Наприклад, у наступному коді:
int* pointer1, pointer2;
pointer2 - це не int * , це простий int . Але з typedefs цього не станеться:
typedef int* pInt;
pInt pointer1, pointer2;
Вони обидва зараз int * .
int *p1 = NULL, *p2 = NULL;
*
не має значення.
Моя відповідь - чітке "Ні".
Чому?
Ну, по-перше, ви просто обмінюєте одного персонажа *
на іншого p
. Це нульовий виграш. Одне лише це повинно заважати вам робити це, оскільки робити погані зайві речі, які безглуздо, завжди погано.
По- друге, і це важлива причина, несе це означає , що не хорошо приховувати . Якщо я передаю щось такій функції*
void foo(SomeType bar);
void baz() {
SomeType myBar = getSomeType();
foo(myBar);
}
Я не сподіваюся, що значення слова myBar
буде змінено, передавши його foo()
. Врешті-решт, я передаю значення, тому foo()
лише коли-небудь бачу копію myBar
так? Не тоді, коли SomeType
псевдонім означає якийсь вказівник!
Це стосується як вказівників на С, так і розумних вказівників на С ++: якщо ви приховуєте той факт, що вони є вказівниками для ваших користувачів, ви створите абсолютно непотрібну плутанину. Тож, будь ласка, не псевдонім своїх покажчиків.
(Я вважаю, що звичка визначати типи вказівників - це просто помилкова спроба приховати, скільки зірок є у програміста http://wiki.c2.com/?ThreeStarProgrammer .)
void *vp=/*...*/; foo(vp);
або foo(0);
. (Typedefs для перелічень та числові typedefs для речей, які ніколи не повинні відображатися, оскільки числа мають подібні проблеми).
Це питання стилю. Цей код ви часто бачите у файлах заголовків Windows. Хоча вони, як правило, віддають перевагу версії з великими літерами, а не префіксом з малої літери p.
Особисто я уникаю такого використання typedef. Набагато зрозуміліше, коли користувач прямо заявляє, що хоче Foo *, ніж PFoo. На сьогоднішній день Typedef найкраще підходять для читання STL :)
typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
Це (як і стільки відповідей) залежить.
У 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.
Ні.
Це зробить ваше життя нещасним, як тільки ви його змішаєте const
typedef foo *fooptr;
const fooptr bar1;
const foo *bar2
Є bar1
і bar2
однотипні?
І так, я просто цитую Гуру Трави Саттера. Багато правди вона говорила. ;)
- Редагувати -
Додавання посилання на цитовану статтю.
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
Дискусія розпочалась, припускаючи, що мова, що цікавить, - C. Впливи на С ++ не розглядались.
Питання Розмір структури, яка визначається як вказівник, викликає цікавий побічний світ щодо використання 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
для визначення вказівника на тип безступінчастої структури.
Якщо ви зробите це, ви не зможете створити STL-контейнери const pSomeStruct, оскільки компілятор читає:
list<const pSomeStruct> structs;
як
list<SomeStruct * const> structs;
який не є законним контейнером STL, оскільки елементи не можна призначити.
Дивіться це питання .
list<const ordinary_type>
, оскільки це б і так не працювало, тому немає плутанини.
ordinary_type
. list<const SomeStruct*>
є цілком дійсним, як const SomeStruct*
і CopyAssignable.
API Win32 робить це практично з усіма структурами (якщо не з усіма)
POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2
Приємно, наскільки це послідовно, але, на мій погляд, це не додає ніякої елегантності.
LP
розшифровується як довгий покажчик, а скорочене позначення LPPOINT ptr;
було нічим іншим, як зручною абревіатурою POINT FAR *ptr;
, що FAR
розширюється до far
та пізніше до __far
. Все це досить неелегантно, але раніше було ще гірше.
Мета typedef - приховати деталі реалізації, але typedef-ing властивості вказівника приховує занадто багато і ускладнює читання / розуміння коду. Тож не робіть цього.
Якщо ви хочете приховати деталі реалізації (що часто добре робити), не приховуйте частину вказівника. Візьмемо, наприклад, прототип для стандартного 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);
Однак це спрацює лише тому, що ви ніколи не балуєтесь із вмістом, на який вказують безпосередньо. У той момент, коли ви набираєте десь вказівник, який ви де-небудь модифікуєте, код стає дуже важким для читання з мого досвіду.
Деякий час тому я відповів би "ні" на це запитання. Зараз, із зростанням розумних покажчиків, покажчики не завжди визначаються зірочкою «*». Отже, немає нічого очевидного в тому, що тип є вказівником чи ні.
Тож зараз я б сказав: добре вводити покажчики def, якщо чітко дається зрозуміти, що це "тип покажчика". Це означає, що ви повинні використовувати префікс / суфікс спеціально для нього. Ні, наприклад, "p" не є достатнім префіксом. Я б, мабуть, пішов із "ptr".