C++ 对于向量,为什么更喜欢迭代器而不是指针?
在Herb Sutter的作品中,他展示了一个将指针放入容器的示例:C++ 对于向量,为什么更喜欢迭代器而不是指针?,c++,pointers,vector,iterator,gotw,C++,Pointers,Vector,Iterator,Gotw,在Herb Sutter的作品中,他展示了一个将指针放入容器的示例: // Example 1: Is this code valid? safe? good? // vector<char> v; // ... char* p = &v[0]; // ... do something with *p ... 此外,该实现存储类型为\u迭代器的成员值,即指针或T*。换句话说,只是一个指针。此外,这种类型的差异类型是std::ptrdiff\u
// Example 1: Is this code valid? safe? good?
//
vector<char> v;
// ...
char* p = &v[0];
// ... do something with *p ...
此外,该实现存储类型为\u迭代器
的成员值,即指针
或T*
。换句话说,只是一个指针。此外,这种类型的差异类型
是std::ptrdiff\u t
,定义的操作只是薄型包装(即操作符+
是++\u指针
,操作符*
是*\u指针
)等等
按照Sutter的论点,这个迭代器类与指针相比没有任何好处,只有缺点。我说的对吗?对于向量,在非泛型代码中,您基本上是正确的 这样做的好处是,您可以将RandomAccessIterator传递给一整套算法,而不管该迭代器迭代的是哪个容器,也不管该容器是否具有连续存储(以及指针迭代器)。这是一个抽象概念 (除其他外,此抽象允许实现将基本指针实现替换为更性感的实现,例如用于调试的范围检查迭代器。) 通常认为使用迭代器是一个好习惯,除非你真的不能。毕竟,习惯孕育一致性,一致性导致可维护性 迭代器也以指针不具备的方式进行自文档化。
int*
指向什么?不知道。std::vector::iterator
指向什么?啊哈
最后,它们提供了一种类型安全性度量——尽管这种迭代器可能只是指针周围的薄型包装器,但它们不必是指针:如果迭代器是不同的类型而不是类型别名,那么您就不会意外地将迭代器传递到您不希望它去的地方,或者意外地将其设置为“NULL”
我同意Sutter的论点和他的大多数其他论点一样有说服力,也就是说不是很有说服力。一个现实生活中喜欢迭代器而不是指针的原因是,迭代器可以在调试构建中实现,并帮助您及早发现一些棘手的问题。即:
vector<int>::iterator it; // uninitialized iterator
it++;
在可以使用指针的地方,不能总是方便地使用迭代器
这不是缺点之一。有时,将指针传递到您确实不希望它们去的地方太“方便”了。使用单独的类型有助于验证参数
一些早期的实现对vector::iterator使用了T*
,但它导致了各种问题,比如人们不小心将不相关的指针传递给了vector成员函数。或者将NULL赋值给迭代器
如果迭代器是一个对象而不仅仅是一个空指针,那么使用迭代器可能会产生额外的空间和性能开销
这是1999年写的,当时我们还认为
中的代码应该针对不同的容器类型进行优化。没过多久,每个人都惊讶地发现,编译器自己就发现了这一点。使用迭代器的通用算法工作得很好
对于std::vector,使用迭代器而不是指针绝对没有时间开销。您发现迭代器类只是指针上的一个薄包装器。编译器也会看到这一点,并生成相应的代码。“使用单独的类型有助于验证参数”它们现在不仅仅是类型别名?好的,啊,是的。我有预感,这与类型安全有关。谢谢你指出我在概念化上遇到的困难。向量迭代器有一个默认构造函数有点奇怪。@Viktor从技术上讲,它不是未初始化的。RandomAccessIterator是ForwardIterator,它必须是可默认构造的(因此,值可初始化)。我引用的实现就是这样做的:
\uuuu normal\u iterator():\u M\u current(\u iterator()){}
当然,这不会使成为it++代码>更多的定义,但仍然是。@user5360395啊,想想看,我想有些算法需要默认构造的迭代器或其他东西。@ViktorSehr不是真的。您可能想要一个未初始化的迭代器,原因与您想要一个未初始化的迭代器的原因相同。(例如,您可能会有条件地为它赋值,然后在以后有条件地使用它)我猜Nemanja的意思是“未初始化”,意思是“单数”或“未初始化为默认值以外的特定值”。我不会这样使用这个术语,标准也不会,但我见过一些人这样做。如果没有限制
,谈论它们中的任何一个的性能都毫无意义。问题似乎在于通过调用指针迭代器的薄薄包装来愚弄自己。如果将指针称为指针,并将迭代器术语保留为只指向容器等的一系列成员中的一个,那么它将成为带保护措施的迭代器和不带保护措施的指针之间的真正选择。
// This iterator adapter is 'normal' in the sense that it does not
// change the semantics of any of the operators of its iterator
// parameter. Its primary purpose is to convert an iterator that is
// not a class, e.g. a pointer, into an iterator that is a class.
// The _Container parameter exists solely so that different containers
// using this template can instantiate different types, even if the
// _Iterator parameter is the same.
vector<int>::iterator it; // uninitialized iterator
it++;
for (it = vec1.begin(); it != vec2.end(); ++it) // different containers