我啥时候应该在 C++ 中使用 typedef?

Posted

技术标签:

【中文标题】我啥时候应该在 C++ 中使用 typedef?【英文标题】:When should I use typedef in C++?我什么时候应该在 C++ 中使用 typedef? 【发布时间】:2010-10-05 16:46:57 【问题描述】:

在我多年的 C++ (MFC) 编程中,我从来没有觉得需要使用 typedef,所以我真的不知道它是用来做什么的。我应该在哪里使用它?是否存在优先使用typedef 的实际情况?或者这真的更像是一个特定于 C 的关键字?

【问题讨论】:

【参考方案1】:

typedef 的 1 个实际示例是 size_t。它保证足够大以包含主机系统可以处理的最大对象的大小。最大允许大小取决于编译器;如果编译器是 32 位,那么对于 unsigned int,它只是一个 typedef,但如果编译器是 64 位,那么对于 unsigned long long,它就是一个 typedefsize_t 数据类型永远不会是负数。

【讨论】:

【参考方案2】:

typedef 不仅允许为复杂类型使用别名,还为您提供了一个自然的地方来记录类型。我有时将其用于文档目的。

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

【讨论】:

【参考方案3】:

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

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

typedef int int32;

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

typedef long int32;

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

【讨论】:

当然你会使用 中的 uint32_t 对吧? :) 仅适用于那些需要 32 位的情况,通常很少见。 @KeithB:我认为稀有性取决于您从事的开发类型。嵌入式系统开发人员和经常处理文件格式的开发人员是我能想到的两种情况,您经常需要知道确切的大小。【参考方案4】:

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; 

【讨论】:

【参考方案5】:

模板元编程

typedef 对于许多template metaprogramming 任务来说是必要的 -- 每当一个类被视为“编译时类型函数”时,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&lt;double*&gt;::type 的计算结果为 double。请注意,模板元编程在库开发之外并不常用。

简化函数指针类型

typedef 有助于为复杂的函数指针类型提供简短而清晰的别名:

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

void RegisterCallback(my_callback_function_type fn) 
    ...

【讨论】:

有必要吗?介意举个例子吗?我想不出任何必要的情况。 对于 C++11,添加“使用 a = b”语法令人愉快地将“typedef”关键字大部分留在记忆中,因为 typedef 总是令人困惑地倒退并且与 #define 不一致(现在我永远不要意外地将两者颠倒,因为它与变量赋值顺序相同)。【参考方案6】:

还有一个使用 typedef 的用例是当我们想要启用一种 Container Independent code(但不完全是!)

假设你有课:

Class CustomerList

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

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

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

【讨论】:

【参考方案7】:

只是为所说的事情提供一些示例:STL 容器。

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

甚至使用 typedef 之类的并不罕见

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

容器类型定义我通常在声明容器变量的地方使用 - 例如local 对于本地 var,当实际容器实例是类成员时,作为类成员。如果实际的容器类型是一个实现细节,这会很好地工作 - 不会导致额外的依赖。

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

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

当类型是不同接口之间的绑定元素时,就会出现问题 - 即多个标头需要相同的类型。一些解决方案:

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

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

【讨论】:

您能讨论一下头文件的最佳实践吗?选项似乎将 typedef 放在 Froboz.h 中,这会产生头文件依赖性和较长的构建时间;将 typedefs 放在 Frobozfwd.h(每个 Effective C++)中,这似乎对可维护性很不利(所有内容都有两个标题);或者将 typedefs 放在 FroCommon.h 中,这会破坏可重用性。有没有更好的办法? 谢谢。我在这里提出了这个问题的更长版本:***.com/questions/2356548/…。恐怕到目前为止我已经得出了相同的结论,那就是没有一个你可以始终如一地使用的好的答案,这意味着很难有一个团队中的每个人都可以遵循和依赖的规则。 “对于这个标头,您需要使用 fwd 版本,但 this 标头只包含基本标头,而 this 相关的东西在 common.h 中定义。 .” 怎么会有人写出可维护和可重用的 C++? (ObjC 宠坏了我……:D)【参考方案8】:

...而且你不需要为枚举或结构使用 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;”在最后一种情况下声明,所以 typedefs 经常用在 C 结构定义中。 为什么你的回答中有问题?【参考方案9】:

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>::T 解释为成员变量名。)【参考方案10】:

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

【讨论】:

如果我想从 int 变成 unsigned long 怎么办?我将不得不检查我所有的源代码是否有溢出等... -> 不是使用 typedef 的好理由!请改用包装接口。 或者给 typedef 一个合理的名称,表明可以依赖哪些属性(例如大小和签名),然后不要以破坏这些属性的方式更改它。 stdint 有一些很好的模型来说明如何做到这一点,例如 int_fast* 和 int_least*。那里不需要大界面。 @xtofl:如果您担心溢出,您可能已经在使用 numeric_limits 执行检查,当您将 my_int 的 typedef'd 更改为时,这些检查将继续执行正确的操作. 如果只使用 int 进行索引,sizeof(int) 通常对应于处理器的 bit'edness 并且是可索引多少内存的限制。因此,如果您可以使用 int,您将永远不会遇到这种情况。【参考方案11】:

与函数指针一起使用

使用 typedef 隐藏函数指针声明

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

只有少数程序员可以说 p 是一个“由 10 个指针组成的数组,指向一个返回 void 的函数,并获取指向另一个返回 void 且不带参数的函数的指针”。繁琐的语法几乎无法辨认。但是,您可以通过使用 typedef 声明大大简化它。首先,为“指向返回 void 且不带参数的函数的指针”声明一个 typedef,如下所示:

  typedef void (*pfv)();

接下来,根据我们之前声明的 typedef 为“指向返回 void 并获取 pfv 的函数的指针”声明另一个 typedef:

 typedef void (*pf_taking_pfv) (pfv);

现在我们已经创建了 pf_taking_pfv 类型定义作为笨拙的“指向返回 void 并获取 pfv 的函数的指针”的同义词,声明一个包含 10 个此类指针的数组是轻而易举的事:

  pf_taking_pfv p[10];

from

【讨论】:

【参考方案12】:

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&lt;int&gt; my_vect”更改为“typedef list&lt;int&gt; my_vect”),一切正常。

typedef 还可以在您拥有写起来很长(且难以阅读)的复杂数据结构时节省您的时间

【讨论】:

这不是使用 typedef 的一个很好的理由:您应该为此使用接口类型(如果您愿意,可以使用抽象数据类型)。这就是为什么您需要添加“取决于代码”。它应该是取决于类型的代码:) C++0x 即将到来! AWW-TO! AWW-TO! AWW-TO! @xtofl:typedef 和接口类型都是解决这个特定问题的有效方法。接口类型更通用,但它们也更重量级。此外,正确使用接口类型意味着所有调用都是虚拟的——迭代器推进/取消引用的代价很高。【参考方案13】:

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

我在 C# 中将 typedef 用于泛型/模板。 “NodeMapping”比很多“Dictionary”更易于阅读/使用和理解。恕我直言。所以我推荐它用于模板。

【讨论】:

以上是关于我啥时候应该在 C++ 中使用 typedef?的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该传递“T* const&”类型的指针?

我啥时候应该在 C 中使用 malloc,啥时候不应该?

我啥时候应该在 C 中使用 malloc,啥时候不应该?

我啥时候应该使用 FutureBuilder?

我啥时候应该在 PL/SQL 中使用过程或函数?

我啥时候应该在 Twitter Bootstrap 中使用容器和行?