Когда следует использовать typedef в C++?

в мои годы программирования на C++ (MFC) я никогда не чувствовал необходимости использовать typedef, поэтому я действительно не знаю, для чего он используется. Где мне его использовать? Существуют ли реальные ситуации, когда использование typedef предпочтительнее? Или это действительно более специфичное ключевое слово C?

12 ответов


Метапрограммирование Шаблона

typedef и необходимые много метапрограммирование шаблона задачи -- всякий раз, когда класс рассматривается как "функция типа времени компиляции", a typedef используется как "значение типа времени компиляции" для получения результирующего типа. Е. Г. рассмотрим простой metafunction для преобразования типа указателя базового типа:

template<typename T>
struct strip_pointer_from;

template<typename T>
struct strip_pointer_from<T*> {   // Partial specialisation for pointer types
    typedef T type;
};

пример: выражение типа strip_pointer_from<double*>::type оценивает в double. Обратите внимание, что шаблон метапрограммирование обычно не используется вне разработки библиотек.

Упрощение Типов Указателей Функций

typedef и полезны для предоставления короткого, резкого псевдонима сложным типам указателей функций:

typedef int (*my_callback_function_type)(int, double, std::string);

void RegisterCallback(my_callback_function_type fn) {
    ...
}

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

на машине, где sizeof (int) равен 4, вы можете

typedef int int32;

затем используйте int32 везде в коде. Когда вы переходите к реализации C++ , где sizeof (int) равен 2, Вы можете просто изменить typdef

typedef long int32;

и ваша программа все равно будет работать над новой реализацией.


использовать указатель на функцию

скрыть объявления указателя функции с помощью typedef

void (*p[10]) (void (*)() );

только немногие программисты могут сказать, что p-это " массив из 10 указателей на функцию, возвращающую void и принимающую указатель на другую функцию, которая возвращает void и не принимает аргументов.- Громоздкий синтаксис почти не поддается расшифровке. Однако, вы можете значительно упростить его, используя объявления typedef. Сначала объявите typedef для " указателя на функцию, возвращающую void и не принимая никаких аргументов" следующим образом:

  typedef void (*pfv)();

затем объявите другой typedef для "указателя на функцию, возвращающую void и принимающую pfv" на основе typedef, который мы ранее объявили:

 typedef void (*pf_taking_pfv) (pfv);

теперь, когда мы создали pf_taking_pfv typedef в качестве синонима громоздкого "указателя на функцию, возвращающую void и принимающую pfv", объявление массива из 10 таких указателей-это бриз:

  pf_taking_pfv p[10];

С


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

 typedef std::map<int,Froboz> tFrobozMap;
 tFrobozMap frobozzes; 
 ...
 for(tFrobozMap::iterator it=frobozzes.begin(); it!=map.end(); ++it)
 {
     ...
 }

нет ничего необычного в том, чтобы даже использовать typedefs как

typedef tFrobozMap::iterator tFrobozMapIter;
typedef tFrobozMap::const_iterator tFrobozMapCIter;

другой пример: использование общих указателей:

class Froboz;
typedef boost::shared_ptr<Froboz> FrobozPtr;

[обновление] согласно комментарию-куда их поставить?

последний пример - использование shared_ptr - легко: истинный материал заголовка - или, по крайней мере, прямой заголовок. Вам все равно нужно объявление forward для shared_ptr, и одним из его заявленных преимуществ является то, что он безопасен в использовании с передним decl.

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

(да, xyzfwd.H-это боль. Я бы использовал их только в горячих точках - зная, что активных точек трудно определить. Вините модель компиляции C+++link...)

Container typedefs я обычно использую, где переменная контейнера объявлено-например, локально для локального var, как члены класса, когда фактический экземпляр контейнера является членом класса. Это хорошо работает, если фактический тип контейнера является детализацией реализации, не вызывая дополнительной зависимости.

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

// FrobozMangler.h
#include "Froboz.h"
typedef std::map<int, Froboz> tFrobozMap;
void Mangle(tFrobozMap const & frobozzes); 

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

  • объявите его вместе с содержащимся типом (подходит для контейнеров, которые часто используются для этого типа)
  • переместить их в отдельный заголовок
  • переместить в отдельный заголовок и сделать его классом данных, где фактический контейнер снова является деталью реализации

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


typedef полезен во многих ситуациях.

в основном это позволяет создать псевдоним для типа. Когда/Если вам нужно изменить тип, остальная часть кода может быть неизменной (это зависит от кода, конечно). Например, предположим, вы хотите iter на векторе c++

vector<int> v;

...

for(vector<int>::const_iterator i = v->begin(); i != v.end(); i++) {

// Stuff here

}

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

typedef vector<int> my_vect;

my_vect v;

...

for(my_vect::const_iterator i = v->begin(); i != v.end(); i++) {

// Stuff here

}

теперь вам просто нужно изменить одну строку кода (i.e от "typedef vector<int> my_vect "to"typedef list<int> my_vect") и все работает.

оператор typedef также экономит ваше время, когда у вас есть сложные структуры данных, которые очень долго писать (и читать сложно)


одна хорошая причина использовать typedef - если тип чего-то может измениться. Например, предположим, что на данный момент 16-битные ints хороши для индексирования некоторого набора данных, потому что в обозримом будущем у вас будет меньше 65535 элементов, и что ограничения пространства значительны или вам нужна хорошая производительность кэша. Однако, если вам нужно использовать программу для набора данных с более чем 65535 элементами, вы хотите легко переключиться на более широкое целое число. Используйте typedef и вы должны изменить это только в одном месте.


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

также бывают случаи, когда я использую массив байтов. Массив байтов может означать многое. typedef позволяет удобно определить мой массив байтов как "hash32" или "fileContent", чтобы сделать мой код более читаемым.


реальное использование typedef:

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

    template<class _T> class A
    {
        typedef _T T;
    };
    
    template<class _T> class B
    {
        void doStuff( _T::T _value );
    };
    

есть еще один вариант использования для использования typedef, когда мы хотим включить своего рода независимый код контейнера (но не точно!)

скажем у вас есть класс:

Class CustomerList{

public:
    //some function
private:
    typedef list<Customer> CustomerContainer;
    typedef CustomerContainer::iterator Cciterator;
};

приведенный выше код инкапсулирует внутреннюю реализацию контейнера с помощью typedef, и даже если в будущем контейнер списка должен быть изменен на vector или deque, пользователю класса CustomerList не нужно беспокоиться о точном контейнере реализация.

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


всякий раз, когда это делает источник яснее или лучше почитайте.

Я использую вид typedef в C# для дженериков / шаблонов. "NodeMapping" просто лучше читать / использовать и понимать, а затем много "Dictionary". ПО МОЕМУ. Поэтому я бы рекомендовал его для шаблонов.


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

typedef <datatype example  int or double> value_type

вы можете дать nay имя вместо value_type, а value_type обычно является стандартным именем.

таким образом, вы можете использовать typedef как

value_type i=0;     //same as a int or double i=0; 

... и Вам не нужен Typedef для перечисления или структуры.

или ты?

typedef enum { c1, c2 } tMyEnum;
typedef struct { int i; double d; } tMyStruct;

может быть лучше записать как

enum tMyEnum { c1, c2 }
struct  tMyStruct { int i; double d; };

это правильно? А как же Си?