与直接使用指针相比,实现和使用迭代器是否会带来任何性能损失?
(假设我们使用最高的编译器优化…)
代码来自http://www.cplusplus.com/reference/iterator/iterator/
// std::iterator example
#include <iostream> // std::cout
#include <iterator> // std::iterator, std::input_iterator_tag
class MyIterator : public std::iterator<std::input_iterator_tag, int>
{
int* p;
public:
MyIterator(int* x) :p(x) {}
MyIterator(const MyIterator& mit) : p(mit.p) {}
MyIterator& operator++() {++p;return *this;}
MyIterator operator++(int) {MyIterator tmp(*this); operator++(); return tmp;}
bool operator==(const MyIterator& rhs) {return p==rhs.p;}
bool operator!=(const MyIterator& rhs) {return p!=rhs.p;}
int& operator*() {return *p;}
};
int main () {
int numbers[]={10,20,30,40,50};
MyIterator from(numbers);
MyIterator until(numbers+5);
for (MyIterator it=from; it!=until; it++)
std::cout << *it << ' ';
std::cout << 'n';
return 0;
}
实现和使用迭代器是否引入了任何类型的性能损失,与直接使用指针相比?
这个问题是有问题的,因为它假设所有迭代器都是指向内存中连续数组的迭代器。但迭代器是对指针的泛化。它也可能是一个指向链表、哈希映射、红黑树等的迭代器,所以在这种情况下,你无法真正比较基于连续数组的迭代器与更复杂类型(如树)的迭代器的性能。
现在,让我换一种方式问这个问题:
对连续数组实现和使用迭代器是否引入了任何类型的性能损失,与直接使用指针相比?
好吧,不是真的,编译器几乎剥离了大多数c++类包装器,并将汇编代码优化为使用C指针生成的相同汇编代码。
你不相信我吗?这里是生成的汇编代码从你的代码,编译与visual studio 2015更新4,x64:int main() {
00007FF7A1D71000 mov qword ptr [rsp+8],rbx
00007FF7A1D71005 push rdi
00007FF7A1D71006 sub rsp,40h
00007FF7A1D7100A mov rax,qword ptr [__security_cookie (07FF7A1D75000h)]
00007FF7A1D71011 xor rax,rsp
00007FF7A1D71014 mov qword ptr [rsp+38h],rax
00007FF7A1D71019 movdqa xmm0,xmmword ptr [__xmm@000000280000001e000000140000000a (07FF7A1D732C0h)]
00007FF7A1D71021 lea rbx,[numbers]
00007FF7A1D71026 movdqu xmmword ptr [numbers],xmm0
00007FF7A1D7102C mov dword ptr [rsp+30h],32h
00007FF7A1D71034 mov edi,5
00007FF7A1D71039 nop dword ptr [rax]
00007FF7A1D71040 mov edx,dword ptr [rbx]
00007FF7A1D71042 mov rcx,qword ptr [__imp_std::cout (07FF7A1D73080h)]
00007FF7A1D71049 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF7A1D73088h)]
00007FF7A1D7104F mov rcx,rax
00007FF7A1D71052 mov dl,20h
00007FF7A1D71054 call std::operator<<<std::char_traits<char> > (07FF7A1D71110h)
00007FF7A1D71059 lea rbx,[rbx+4]
00007FF7A1D7105D sub rdi,1
00007FF7A1D71061 jne main+40h (07FF7A1D71040h)
00007FF7A1D71063 mov rcx,qword ptr [__imp_std::cout (07FF7A1D73080h)]
00007FF7A1D7106A mov dl,0Ah
00007FF7A1D7106C call std::operator<<<std::char_traits<char> > (07FF7A1D71110h)
00007FF7A1D71071 xor eax,eax
}
下面是使用c指针的代码:
int main() {
int numbers[] = { 10,20,30,40,50 };
for (MyIterator it = numbers; it != numbers + 5; it++)
std::cout << *it << ' ';
std::cout << 'n';
return 0;
}
int main() {
00007FF6A72E1000 mov qword ptr [rsp+8],rbx
00007FF6A72E1005 push rdi
00007FF6A72E1006 sub rsp,40h
00007FF6A72E100A mov rax,qword ptr [__security_cookie (07FF6A72E5000h)]
00007FF6A72E1011 xor rax,rsp
00007FF6A72E1014 mov qword ptr [rsp+38h],rax
00007FF6A72E1019 movdqa xmm0,xmmword ptr [__xmm@000000280000001e000000140000000a (07FF6A72E32C0h)]
00007FF6A72E1021 lea rbx,[numbers]
00007FF6A72E1026 movdqu xmmword ptr [numbers],xmm0
00007FF6A72E102C mov dword ptr [rsp+30h],32h
00007FF6A72E1034 mov edi,5
00007FF6A72E1039 nop dword ptr [rax]
00007FF6A72E1040 mov edx,dword ptr [rbx]
00007FF6A72E1042 mov rcx,qword ptr [__imp_std::cout (07FF6A72E3080h)]
00007FF6A72E1049 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF6A72E3088h)]
00007FF6A72E104F mov rcx,rax
00007FF6A72E1052 mov dl,20h
00007FF6A72E1054 call std::operator<<<std::char_traits<char> > (07FF6A72E1110h)
00007FF6A72E1059 lea rbx,[rbx+4]
00007FF6A72E105D sub rdi,1
00007FF6A72E1061 jne main+40h (07FF6A72E1040h)
00007FF6A72E1063 mov rcx,qword ptr [__imp_std::cout (07FF6A72E3080h)]
00007FF6A72E106A mov dl,0Ah
00007FF6A72E106C call std::operator<<<std::char_traits<char> > (07FF6A72E1110h)
00007FF6A72E1071 xor eax,eax
}
如果迭代器函数可以内联,则抽象的运行时成本为0。
这就是Stroustrup在c++基础中所说的零开销抽象:
一般来说,c++实现遵循零开销原则:你不用的东西,你就不用付钱。进一步说:你用的代码,你不能再写得更好了。
零开销抽象机制。通过"轻量级抽象",我指的是不会额外增加空间或时间开销的抽象对抽象的一个特定的例子进行仔细的手工编码。