在我多年的C++ (MFC)编程中,我从未感到需要使用typedef
,所以我不太知道它的用途。我应该在哪里使用它?是否有任何情况下优先使用typedef
?还是这个关键字只用于C语言?
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) {
...
}
在Bjarne的书中,他提到你可以使用typedef来处理不同整数大小的系统之间的可移植性问题。(这是一个意译)
在一个 sizeof(int)
为4的机器上,你可以这样做
typedef int int32;
那么在你的代码中到处使用 int32
。 当你迁移到一个C++实现,其中sizeof(int)
为2时,你可以只更改 typedef
。
typedef long int32;
并且您的程序仍将在新实现上正常工作。
使用函数指针
使用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 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)
{
...
}
即使使用typedef也并非不寻常
typedef tFrobozMap::iterator tFrobozMapIter;
typedef tFrobozMap::const_iterator tFrobozMapCIter;
另一个例子:使用共享指针:
class Froboz;
typedef boost::shared_ptr<Froboz> FrobozPtr;
[更新] 根据评论 - 放在哪里?
最后一个例子 - 使用 shared_ptr
- 是很容易的:它们是真正的头文件材料 - 或者至少是前向声明头文件。你无论如何都需要 shared_ptr 的前向声明,并且其声明的优点之一是可以安全地使用前向声明。
换句话说:如果有一个 shared_ptr,你可能只应该通过 shared_ptr 使用该类型,因此将声明分开并没有太多意义。
(是的,xyzfwd.h 很让人痛苦。我只会在热点区域中使用它们 - 知道热点很难被识别。怪C ++编译+链接模型...)
容器 typedef 我通常在声明容器变量的位置使用 - 例如,在本地变量中本地使用,作为类成员当实际容器实例是类成员时。如果实际容器类型是实现细节,则此方法效果很好 - 不会增加其他依赖。
如果它们成为 特定 接口的一部分,则与它们一起使用的接口一起声明,例如:
// FrobozMangler.h
#include "Froboz.h"
typedef std::map<int, Froboz> tFrobozMap;
void Mangle(tFrobozMap const & frobozzes);
当类型是不同接口之间的绑定元素时,这会变得棘手 - 即多个头需要相同的类型。一些解决方案:
我同意后两者不是那么好,只有在遇到问题时才会使用它们(不是主动使用)。
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”还可以节省您的时间。
使用typedef的一个好处是,如果某个东西的类型可能会改变,你可以使用它。例如,假设目前使用16位整数对某个数据集进行索引是可以的,因为在可预见的未来,您将拥有少于65535个项目,并且空间限制非常重要,或者需要良好的缓存性能。但是,如果万一您需要在包含超过65535个项目的数据集上使用程序,您希望能够轻松地切换到更广的整数。使用typedef,您只需要在一个地方进行更改。
typedef
不仅允许为复杂类型创建别名,还提供了一个自然的地方来记录类型。我有时用它来进行文档编写。
有时候我会使用一个字节数组。现在,字节数组可能意味着很多东西。typedef
使定义我的字节数组为“hash32”或“fileContent”变得更加方便,以使我的代码更易读。
typedef的现实世界应用:
providing local labels for types, e.g.:
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;
};
每当它使源代码更清晰或更易于阅读时。
我在C#中使用了一种类似于typedef的东西来处理泛型/模板。一个"NodeMapping"比很多"Dictionary<string, XmlNode>"更易于阅读、使用和理解。在我看来,所以我建议在模板中使用它。