Когда я должен использовать typedef в C ++? - PullRequest
64 голосов
/ 05 февраля 2009

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

Ответы [ 12 ]

83 голосов
/ 05 февраля 2009

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

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

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) {
    ...
}
33 голосов
/ 05 февраля 2009

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

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

typedef int int32;

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

typedef long int32;

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

21 голосов
/ 05 февраля 2009

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

Скрыть объявления указателей функций с помощью 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 в качестве синонима громоздкого «указателя на функцию, возвращающую void и принимающую pfv», объявление массива из 10 таких указателей является легким:

  pf_taking_pfv p[10];

из

17 голосов
/ 05 февраля 2009

Просто приведу несколько примеров сказанного: контейнеры 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 - прост: это настоящий материал заголовка или, по крайней мере, прямой заголовок. В любом случае вам необходимо предварительное объявление для shared_ptr, и одно из объявленных преимуществ заключается в том, что его можно безопасно использовать с предварительным decl.

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

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

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

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

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

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

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

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

5 голосов
/ 05 февраля 2009

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

5 голосов
/ 05 февраля 2009

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

В основном это позволяет вам создать псевдоним для типа. Когда / если вам нужно изменить тип, остальная часть кода может быть неизменной (это, конечно, зависит от кода). Например, допустим, вы хотите использовать вектор 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

}

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

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

4 голосов
/ 19 марта 2013

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

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

2 голосов
/ 20 июля 2015

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

Допустим, у вас есть класс:

Class CustomerList{

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

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

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

2 голосов
/ 05 февраля 2009

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

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

    template<class _T> class A
    {
        typedef _T T;
    };
    
    template<class _T> class B
    {
        void doStuff( _T::T _value );
    };
    
0 голосов
/ 12 февраля 2017

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

typedef <datatype example  int or double> value_type

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

Так что вы можете использовать typedef как

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