什么时候应该在C ++中使用typedef?


68

在我从事C ++(MFC)编程的那几年中,我从没有感觉到需要使用typedef,因此我真的不知道它的用途。我应该在哪里使用?在某些实际情况下,typedef首选使用吗?还是这真的是特定于C的关键字?

Answers:


88

模板元编程

typedef对于许多模板元编程任务来说是必需的-每当将一个类视为“编译时类型函数”时,就将a用作“编译时类型值”以获取结果类型。例如,考虑将指针类型转换为其基本类型的简单元函数: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) {
    ...
}

1
必要?想举个例子吗?我想不出有必要的任何情况。
jalf

10
对于C ++ 11,添加“ using a = b”语法令人愉快地将“ typedef”关键字大部分留给了内存,因为typedef总是令人困惑地倒退,并且与#define不一致(现在,我从来不会无意间将两者反向,因为它是与变量分配顺序相同)。
Dwayne Robinson

37

在Bjarne的书中,他指出可以使用typedef处理具有不同整数大小的系统之间的可移植性问题。(这是一个释义)

sizeof(int)4的机器上,您可以

typedef int int32;

然后int32在代码中的任何地方使用。当您转到sizeof(int)2的C ++实现时,只需更改typdef

typedef long int32;

并且您的程序仍将在新的实现上运行。


12
当然,您会使用来自<stdint.h>的uint32_t,对吗?:)
格雷格·罗杰斯

并且仅在通常需要的情况下,需要精确的32位。
KeithB

7
@KeithB:我认为稀有性取决于您进行哪种开发。嵌入式系统开发人员和经常处理文件格式的人员是两种情况,我可以想到何时需要经常知道确切的大小。
j_random_hacker

22

与函数指针一起使用

使用typedef隐藏函数指针声明

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

只有很少的程序员可以说p是“由10个指向返回void的函数的指针组成的数组,并采用指向另一个返回void并且不带参数的函数的指针组成的数组”。繁琐的语法几乎难以理解。但是,您可以使用typedef声明将其大大简化。首先,为“指向返回空值且不带参数的函数的指针”声明一个typedef,如下所示:

  typedef void (*pfv)();

接下来,根据先前声明的typedef声明另一个typedef,以表示“指向返回空值并获取pfv的函数的指针”:

 typedef void (*pf_taking_pfv) (pfv);

现在,我们已经创建了pf_taking_pfv typedef作为笨拙的“指向返回void并获取pfv的函数的指针”的同义词,声明一个由10个这样的指针组成的数组很容易:

  pf_taking_pfv p[10];


18

只是为了提供一些例子说明事情:STL容器。

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

甚至使用像

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 ++编译+链接模型...)

容器typedef我通常在声明容器变量的地方使用-例如,当实际容器实例是类成员时,在本地var中作为类成员。如果实际容器类型是实现详细信息,则此方法效果很好-不会引起任何其他依赖关系。

如果它们成为特定接口的一部分,则将它们与使用的接口一起声明,例如

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

当类型是不同接口之间的绑定元素时,即成为问题,即多个报头需要相同的类型。一些解决方案:

  • 与包含的类型一起声明(适用于此类型经常使用的容器)
  • 将它们移动到单独的标题
  • 移动到单独的标头,并使其成为数据类,其中实际容器再次是实现细节

我同意后两者并不是那么好,我只会在遇到麻烦时才使用它们(不是主动地)。


您可以讨论有关头文件的最佳做法吗?这些选项似乎是将typedef放在Froboz.h中,这会创建标头依赖性和较长的构建时间。将typedef放在Frobozfwd.h中(根据有效的C ++),这似乎给可维护性带来了痛苦(所有内容都有两个标头);或将typedef放在FroCommon.h中,这会破坏可重用性。有更好的方法吗?
罗布·纳皮尔

1
谢谢。我将这个问题的较长版本放在这里:stackoverflow.com/questions/2356548/…。恐怕到目前为止,我仍得出相同的结论,那就是您并不能始终使用一个很好的答案,这意味着很难有一个规则可以让团队中的每个人都可以遵循和依赖。“对于此标头,您需要使用fwd版本,但标头仅包含基本标头,并且相关的内容在此处common.h中定义。”任何人如何编写可维护和可重用的C ++?(ObjC宠坏了我...:D)
Rob Napier 2010年

5

typedef在许多情况下很有用。

基本上,它允许您为类型创建别名。当/如果您必须更改类型,则其余代码可能保持不变(当然,这取决于代码)。例如,假设您要遍历C ++向量

vector<int> v;

...

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

// Stuff here

}

将来您可能会考虑使用列表更改向量,因为您必须对其执行操作的类型。如果没有typedef,则必须更改代码中所有出现的vector。但是,如果您编写这样的内容:

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还可以节省您的时间


1
使用typedef并不是一个很好的理由:您应该为此使用接口类型(如果愿意,可以使用Abstract Data Type)。这就是为什么您需要添加“取决于代码”的原因。它应该是取决于类型的代码:)
xtofl

C ++ 0x即将来临!AWW-TO!AWW-TO!AWW-TO!
David Thornley,2009年

3
@xtofl:typedef和接口类型都是解决此特定问题的有效方法。接口类型更通用,但也更重量级。同样,正确使用接口类型意味着所有调用都将是虚拟的,这对于迭代器的推进/取消是沉重的代价。
j_random_hacker

5

使用typedef的一个好理由是某些东西的类型可能会改变。例如,假设现在,16位int可以很好地为某些数据集建立索引,因为在可预见的将来,您将拥有少于65535个项目,并且空间限制很大,或者您需要良好的缓存性能。但是,如果您需要在包含超过65535个项目的数据集上使用程序,那么希望能够轻松切换到更大的整数。使用typedef,您只需在一个地方进行更改。


1
如果我想从int更改为unsigned long怎么办?我将不得不检查我所有的源代码是否存在溢出等...->不是使用typedef的好理由!请改用包装器接口。
xtofl,2009年

或者给typedef一个明智的名称,该名称指示可以依赖哪些属性(例如大小和签名),然后不要以破坏这些属性的方式对其进行更改。stdint有一些很好的模型来做到这一点,例如int_fast *和int_least *。那里不需要大接口。
史蒂夫·杰索普

@xtofl:如果您担心溢出,您已经在使用numeric_limits <my_int>进行检查,并且当您将my_int更改为typedef时,这些检查将继续执行正确的操作。
j_random_hacker

如果仅使用int进行索引,则sizeof(int)通常对应于处理器的位容量,并且是可索引多少内存的限制。因此,如果可以使用int,则永远不会出现这种情况。
Joseph Garvin

4

typedef不仅为复杂类型提供别名,还为您提供了记录类型的自然位置。我有时将其用于文档目的。

有时候我使用字节数组。现在,字节数组可能意味着很多事情。typedef可以方便地将字节数组定义为“ hash32”或“ fileContent”,以使代码更具可读性。


2

typedef的实际用法:

  • 为冗长的模板化类型提供友好的别名
  • 为函数指针类型提供友好的别名
  • 提供类型的本地标签,例如:

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

我认为不会编译。您可能是说“ void doStuff(typename A <_T> :: T _value);”吗?(您需要在其中使用typename关键字,因为编译器会将A <_T>
:::

2

使用typedef的另一个用例是当我们要启用一种 容器独立代码(但不完全是!)时。

让我们说您上课了:

Class CustomerList{

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

上面的代码使用typedef封装了内部容器的实现,即使将来需要将列表容器更改为vector或双端队列,CustomerList类的用户也不必担心确切的容器实现。

因此,typedef封装并在某种程度上帮助我们编写了独立于容器的代码


0

只要它使源代码更清晰或更易于阅读。

我在C#中将某种类型定义用于泛型/模板。与许多“ Dictionary <string,XmlNode>”相比,“ NodeMapping”更易于阅读/使用和理解。恕我直言。因此,我建议将其用于模板。


0

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; 

-2

...并且您不需要枚举或结构的Typedef。

还是你

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

可以更好地写成

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

那是对的吗?C呢?


在C语言中,您需要说“ struct tMyStruct foo;”。在最后一种情况下进行声明,因此typedef通常在C结构定义中使用。
David Thornley,2009年

9
为什么答案中有问题?
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.