Swap Bug in VC8 vs
Swap Fix in VC9
作者:tyc611.cublog.cn,2008-08-31
VC8引入了Debug Iterator和Checked Iterator,但却存在一个严重的Bug。在描述此Bug之前,我们先来看看描述Debug Iterator和Checked Iterator的内部工作原理。
为了使Iterator具有检错功能,Debug Iterator和Checked Iterator内部都添加了额外的数据,例如指向父容器的指针。而对于Debug Iterator,容器中还提供了一个单向链表,用来记录该容器的所有Iterator。通过这些额外的指针,迭代器就可以做一些检查功能,比如参与运算的两个迭代器是否来自同一容器、迭代器是否已经失效,等等。
现在,让我们来看看VC8存在的Bug,也是就是所谓
Swap Bug。当禁用_HAS_ITERATOR_DEBUGGING(Debug Iterator)和启用_SECURE_SCL(Checked Iterator)时,如果我们交换(Swap)两个容器对象,此时就会出现Swap Bug。在使用Checked Iterator时,容器并没有相应的方法能够找到它的迭代器(只有迭代器通过内部的指针找到容器)。因此,在两容器对象交换后,它们的所有迭代器都指向了错误的容器对象,因此进一步使用这些迭代器时就会出现错误。而C++标准(23.1/10)要求容器对象交换后,迭代器仍然有效。这样,就产生了Swap Bug。由于Debug Iterator的容器有一个所有迭代器的单向链表,在交换时能够更新相应迭代器的父容器指针,因此不会出现Swap Bug。
为了解决这个Bug,VC9为每个容器(
std::string除外)添加了一个额外的动态分配的对象,被称为"aux object“(辅助对象)。容器拥有一个指针指向它的“aux object”,而该“aux object”拥有一个指针回指容器。此时,Checked Iteratro以前的父容器指针改为指向父容器的“aux object”。在交换容器对象时,同时交换它们的“aux object”对象即可,这样就解决了之前的Swap Bug。
但新的解决方案有两个缺点:其一,由于引入了一个间接层,效率有所下降;其二,容器占用了额外的空间。
前面提到,VC9为其它容器都进行Bug Fix,唯独没有修改std::string(及相应的std::wstring)。原因在于,std::string(及std::wstring)被直接编译在msvcp90[d].dll中,并在编译时设置_SECURE_SCL=1(Debug版还设置了_HAS_ITERATOR_DEBUGGIN=1)。因此,在使用string(及wstring或者basic_string)时,如果使用它的迭代器就需要注意避开Swap Bug。事实上,大多数时候,我们都是使用它的下标索引形式来访问字符串数据。作为用户的我们也只能希望在VC10中能够解决这个问题。
另外,由于_SECURE_SCL和_HAS_ITERATOR_DEBUGGIN设置不同,产生的代码中容器对象和迭代器对象也会有所不同。因此,在使用
Debug Iterator和Checked
Iterator时,我们务必保证所有编译单元都使用了相同的_SECURE_SCL和_HAS_ITERATOR_DEBUGGIN设置。目前编译器还无法检测到这个不一致问题,不知道在VC10中是否能够提供相应的检测功能。
由于在标准算法实现中,迭代器检查被提到循环外面,因此建议尽量使用C++标准算法,而不要手写循环。这样,可以有效地减少迭代器内部的检查次数,从而提高程序运行效率。
最后,附上一小段程序,让我们来看看在启用和禁用_SECURE_SCL的情形下,容器大小的变化。
程序源码:#include
#include
using namespace std;
int main()
{
vector v;
cout << sizeof(v) << endl;
return 0;
}
启用_SECURE_SCL(默认):
F:\tmp>cl /EHsc /nologo test.cpp
test.cpp
F:\tmp>test.exe
24
禁用_SECURE_SCL:
F:\tmp>cl /EHsc /D "_SECURE_SCL=0" /nologo test.cpp
test.cpp
F:\tmp>test.exe
16
参考资料:
1. http://blogs.msdn.com/vcblog/archive/2007/08/10/the-future-of-the-c-language.aspx
阅读(1767) | 评论(1) | 转发(0) |