我有同事偶尔会使用typedef来避免打字。例如:
typedef std::list<Foobar> FoobarList;
...
FoobarList GetFoobars();
就个人而言,我总是讨厌遇到这样的代码,很大程度上是因为它迫使我去查找typedef所以我可以告诉如何使用它。我也觉得这种事情是一个潜在的滑坡...如果你这样做,为什么你不这样做呢? (很快,你的代码完全被混淆了)。我在这个问题上找到了这个问题:
when should I use typedef in C
我有两个问题:1)我真的一个人不喜欢这个吗? 2)如果绝大多数人认为这种typedef使用正常,你用什么标准来确定是否要输入一个类型?
答案 0 :(得分:18)
这种类型的typedef
的两个主要论点是减少的键入,你已经提到过,以及转换到新类型容器的容易程度。 FoobarList可以由vector
或list
或deque
支持,切换通常只需要更改typedef。
在处理IDE时,你不喜欢它们,因为我只能将鼠标悬停在类型名称上,IDE会告诉我它的定义是什么。
当你有嵌套容器时,更有用的情况是 - 你可以给名字一些语义含义,而不必定义整个类:
typedef std::list<Foobar> FoobarList;
typedef std::map <string, FoobarList> GizmosToFoobarsMap;
在处理这些类型的迭代器时,您还可以保存很多类型(尽管现在C ++ 0x已经auto
减少了。)
答案 1 :(得分:9)
typedef
对STL和模板编程至关重要。看看迭代器特征是如何工作的:
template <class Iterator>
struct iterator_traits {
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
};
template <class T>
struct iterator_traits<T*> {
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
关于typedef
s的“快捷方式”用法 - 我认为在本地化的实现文件中完全没问题。我在这里和using namespace
有相同的规则 - 如果它可以节省我的打字并且不会让其他人感到困惑,那就去吧。
答案 2 :(得分:7)
我喜欢这样的typedef,因为它们提高了可读性。考虑在整个地方std::vector<std::string>::const_iterator
,甚至std::map<std::string, std::vector<std::string> >::iterator
。一半的代码最终只是描述类型,而一堆typedef简化了很多。我不认为这也是一个障碍太多了 - 如果你把鼠标悬停在它上面或者去定义,IDE通常可以告诉你类型,并且使用代码通常需要你理解这样的事情。 / p>
答案 3 :(得分:5)
我喜欢typedef
,因为它允许稍后 更改容器的类型 (比如,分析显示std::deque
要比std::list
快,而不会更改所有有问题的代码 。
如果你想知道:我不反对被称为列表的东西,即使它可能是std::deque
。在这种情况下,我将“列表”视为概念术语,而不是数据结构。 (你不把你的购物清单当作双重链表,是吗?)
答案 4 :(得分:3)
我个人也typedef
STL的东西,列表和迭代器(Foos
和FooIterator
)。在任何地方使用都会产生一种非常清晰的(imo)风格,你会在第二次出现时习惯这种风格。
此外,您可以将鼠标悬停在该类型上,以查看它在任何真实IDE(vs或eclipse)中的真正含义,因此除了不必滚动3个屏幕外,您还可以在其使用点获得所需的所有信息在右边只读全文。
使用C ++ 0x auto
关键字,将不再需要此关键字。是的,我是C#的var
关键字的相信者,如果您有疑问,还可以将鼠标悬停在它上面以查看类型!
答案 5 :(得分:2)
我也没有在我的代码中这样做,所以我猜你并不孤单。在我看来,它还有一个让读者不得不去查看的东西。糟糕的代码阅读器通常已经绰绰有余了。
答案 6 :(得分:2)
在您的示例中,FoobarList
与其他任何类都没有区别;您仍然需要查看定义或文档以了解如何使用它;并且你不会将它用作不使用类的参数!
在这种情况下,一旦你确定它是STL std :: list,你就会知道如何使用它,或者至少知道在哪里找到高质量的文档;对于可能在项目中定义的大多数类,这可能比你可以说的要多。
那说我对这是否是个好主意感到矛盾;我只是认为它几乎无害。
答案 7 :(得分:1)
我个人发现typedef在处理模板化代码时非常有用,无论是在编写模板时还是在实例化它们时。
编写模板:
typedef是模板元编程所必需的。例如,删除const:
template<typename T>
struct RemoveConst
{
typedef T Type;
};
template<>
struct RemoveConst<const T>
{
typedef T Type;
};
现在,我们可以通过实例化RemoveConst :: Type
从任何T访问const-less类型实例化模板:
像编程中的很多东西一样,正确的工作正确的工具。 typedef,例如你的例子
typedef std::list<Foobar> FoobarList;
...
FoobarList GetFoobars();
声音完全合理,主要是因为typedefed名称是描述性的(FoobarList是Foobars列表)。这在处理STL容器或共享相同接口的任何其他模板类型时尤其有用。想象下面的类声明:
class SomeClass
{
...
std::vector<int> mContainer;
};
这个类可能会迭代Container的元素,这会导致代码类似于:
for(std::vector<int>::iterator It = mContainer.begin(); It != mContainer.end(); ++It)
{
}
现在想象一下,在用5个不同的方法编写上面的for循环之后,你会意识到你经常插入数组的中间,并且std :: list会更适合这个工作。 在这种情况下,您必须遍历std :: vector :: iterator的每个实例,并更改声明。
相反,您可以做的是在类中使用typedef容器:
class SomeClass
{
typedef std::vector<int> IntContainer;
...
IntContainer mContainer;
};
这可以让你编写高度通用的代码,这些代码很容易修改。
for(IntContainer::iterator It = mContainer.begin(); It != mContainer.end(); ++It)
{
}
在这种情况下,您只需要更改IntContainer的typedef,并且任何引用它的实例都会自动更改。
答案 8 :(得分:0)
当您需要编写跨平台工作的C ++代码时,您会看到很多typedef,因为基本数据类型的大小并不固定。例如,Java虚拟机具有用于不同编译器的基本类型的typedef。
答案 9 :(得分:0)
我认为在这种情况下,typedef是很好的做法。虽然模板语法将参数放在你面前,但它们通常是人们试图在面向对象环境中封装的东西。
typedef'ing模板执行封装。
答案 10 :(得分:0)
如果我希望结束类型是强类型的,我通常更喜欢继承而不是typedef,例如
class CMyMap : public CMap<int, int> {...}
这样,CMyMap是强类型的 - 我只是不能将任何CMap<int, int>
传递给一个以CMyMap作为参数的方法。 typedef只告诉编译器“另一个名称”,但类定义定义了一个新类型。
如果我定义了一个函数 void foo(const CMyMap&amp; map);
但我使用typedef来定义CMyMap,例如
typdef CMap<int, int> CMyMap;
然后有人可以做
CMap<int, int> myMap;
FOO(MYMAP);
并且编译器会非常高兴。
但如果我真的想要foo强类型并且只接受CMyMap,那么继承是我选择“打字”的机制。
BTW,CMyMap是通用的,但更具体的可能是CPhoneNumber2Index,其中电话号码表示为整数,索引是整数。很明显,我的CPhoneNumber2Index不仅仅是从整数到另一个整数的映射。所以foo可能会 string ReverseLookup(const CPhoneNumber2Index&amp; PhoneBook,int iPhoneNumber); 这表明ReverseLookup与电话号码有关,而不是CMap<int, int>
答案 11 :(得分:0)
在对模板进行一些严重的混乱之后,typedef的行为就像正常的变量赋值...