VC 之 Checked Iterator
作者:tyc611.cublog.cn,2008-08-27
Checked
Iterator是指具有越界检查功能的迭代器,并且会在检查到越界操作时触发运行时错误处理(调用非法参数处理例程或者抛出异常)。VC从VS2005
开始支持Checked Iterator。另外,VC还支持Debug Iterator,有更多的检查功能,这里不予讨论。
Part One 编译
Checked
Iterator能够确保迭代器不会发生越界访问(如果发生越界访问,则会进行相应的处理)。如果在需要Checked
Iterator的地方使用了非Checked Iterator,则在编译时会产生编译警告C4996(Level
3)。如果需要禁用此警告,可以通过定义宏_SCL_SECURE_NO_WARNINGS来达到目的。
#define _SCL_SECURE_NO_WARNING
|
可以通过定义宏_SECURE_SCL的值来启用/禁用Checked
Iterator的检查功能。如果宏_SECURE_SCL被定义为1,则启用Checked
Iterator的检查功能;如果宏_SECURE_SCL被定义为0,则禁用Checked Iterator的检查功能。如果Checked
Iterator发现迭代器越界,则会触发运行时错误,该运行时错误的具体行为与宏_SECURE_SCL_THROWS的值相关。默认情况
下,_SECURE_SCL的值为1。(注:Checked Iterator功能的启用与禁用和Debug
Iterator功能的启用与禁用之间不会彼此影响)
宏_SECURE_SCL_THROWS的值决定了Checked
Iterator在发现越界时的运行时报错方式。如果_SECURE_SCL_THROWS被定义为1,则Checked
Iterator在发现越界时会抛出异常std::out_of_rangce;如果_SECURE_SCL_THROWS被定义为0,则会调用CRT的
非法参数处理例程(默认是终止程序)。宏_SECURE_SCL_THROWS的默认值为0。由于CRT的非法参数处理例程可以自行设置,所以程序可以改
变默认终止运行的行为。(注:只有在_SECURE_SCL被定义为1时,_SECURE_SCL_THROWS才起作用)
示例代码如下:
// cl /EHsc test1.cpp
#include
#include
using namespace std;
void myInvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
cout << "just ignore it" << endl;
}
int main()
{
vector v;
v.push_back(11);
v.push_back(22);
cout << v[1] << endl;
_set_invalid_parameter_handler(myInvalidParameterHandler);
cout << v[2] << endl; // out of range!
return 0;
}
程序运行结果:
22
just ignore it
131074
另外,如果定义_SECURE_SCL_THROWS为1,则在Debug版程序中需要在捕获异常之前先调用
_CrtSetReportMode(_CRT_ASSERT,
0)禁用ASSERT报错,并在捕获异常之后恢复。因为在程序抛出异常之前会先触发断言失败。示例代码如下:
// cl /EHsc /D_DEBUG /MDd test2.cpp
#define _SECURE_SCL_THROWS 1
#include
#include
#include // For _CrtSetReportMode
using namespace std;
int main()
{
vector v;
v.push_back(11);
v.push_back(22);
cout << v[1] << endl;
// disable ASSERT
int saved = _CrtSetReportMode(_CRT_ASSERT, 0);
try {
cout << v[2] << endl; // out of range
}
catch (std::out_of_range &) {
cout << "out of range" << endl;
}
// restore ASSERT
_CrtSetReportMode(_CRT_ASSERT, saved);
return 0;
}
程序运行结果:
22
out of range
Part Two 库
迭代器与容器
如果宏_SECURE_SCL为1,则所有标准库容器的迭代器都是Checked Iterator;如果宏_SECURE_SCL为0,则所有标准库容器的迭代器都是Unchecked Iterator。
数组、指针及自定义迭代器类型均是Uncheck
Iterator,但可以通过迭代器适配器stdext::checked_array_iterator和
stdext::checked_iterator把它们适配为Checked
Iterator(注:VC对C++的所有扩展名字都位于名字空间stdext中)。例如,下面的代码片断使用
checked_array_iterator把一个数组(也可以是指针)适配为一个Checked Iterator:
// 示例代码片断
int a[]={1, 2, 3, 4, 5, 6};
int b[6];
copy(a, a + 6, stdext::checked_array_iterator(b, 5));
如果宏_SECURE_SCL为1,则下面这些容器成员函数均会进行越界检查(Checked Iterator功能):
basic_string::operator[]、
bitset::operator[]、deque::back、deque::front、deque::operator[]、
list::back、list::front、queue::back、queue::front、valarray::operator[]、
vector::back、vector::front、vector::operator[]。
算法
VC对C++标准库中部分算法进行了扩展。所有扩展算法均有一个对应的标准算法,且都位于名字空间stdext中,所在头文件与对应的标准算法相同,参数
与用法也一样,唯一的区别是对参数迭代器的要求有所不同。这些扩展算法有两个版本,一个checked版本和一个unchecked版本,其函数名就是在
标准算法的函数名之前添加前缀checked_和unchecked_。例如,标准算法std::copy有两个对应的扩展算
法:stdext::check_copy和stdext::unchecked_copy。
当_SECURE_SCL为1时,
如果标准算法有扩展版本,则此时标准算法相当于扩展算法的checked版本。
如果作为算法参数的输出迭代器是一个Checked
Iterator,那么在调用标准算法、checked版扩展算法和unchecked版扩展算法时,都将进行越界检查(例如,std::copy、
stdext::checked_copy和stdext::unchecked_copy)。
如果作为算法参数的输出迭代器是一个Unchecked Iterator,那么在调用标准算法和checked版算法时,将产生编译警告(C4996);而在调用unchecked版算法时不会产生警告信息。此时,都不会进行越界检查。
当_SECURE_SCL为0时,
如果标准算法有扩展版本,则此时标准算法相当于扩展算法的unchecked版本。
如果作为算法参数的输出迭代器是一个Checked Iterator,那么在调用标准算法、checked版扩展算法和unchecked版扩展算法时,都将进行越界检查。
如果作为算法参数的输出迭代器是一个Unchecked Iterator,那么在调用checked版算法时,将产生编译警告(C4996);而在调用标准算法和unchecked版本算法时不会产生警告信息。此时,都不会进行越界检查。
VC的扩展算法有(注:这里只列举checked版,unchecked版与checked版一一对应,只需要把函数名前缀checked_改为unchecked_即可):
checked_adjacent_difference、checked_copy、checked_copy_backward、
checked_fill_n、checked_generate_n、checked_merge、checked_partial_sum、
checked_remove_copy、checked_remove_copy_if、checked_replace_copy、
checked_replace_copy_if、checked_reverse_copy、checked_rotate_copy、
checked_set_difference、checked_set_intersection、
checked_set_symmetric_difference、checked_set_union、
checked_uninitialized_copy、checked_uninitialized_fill_n、
checked_unique_copy。
其它编译问题
请尝试编译如下代码(注意使用下面提供的编译选项!):
// 示例代码
// cl /EHsc /D_DEBUG /MDd /Za test.cpp
#define _SECURE_SCL_THROWS 1
#include
int main()
{
return 0;
}
我的VC9 SP1报错如下:
test.cpp
D:\Microsoft Visual Studio 9.0\VC\INCLUDE\xstring(1566) : error C3861: “_Xran”
: 找不到标识符
D:\Microsoft Visual Studio 9.0\VC\INCLUDE\xstring(1557): 编译类 模板 成
员函数“char &std::basic_string<_Elem,_Traits,_Ax>::operator [](unsigned int)”
时
with
[
_Elem=char,
_Traits=std::char_traits,
_Ax=std::allocator
]
D:\Microsoft Visual Studio 9.0\VC\INCLUDE\xstring(2221): 参见对正在编译
的类 模板 实例化“std::basic_string<_Elem,_Traits,_Ax>”的引用
with
[
_Elem=char,
_Traits=std::char_traits,
_Ax=std::allocator
]
看到上面的源代码和报错信息,估计有人会发疯了(我无意中挖掘出这个编译场景,抓狂了半天!)。确实,代码本身是没有任意问题的(根本就没有一行自己的代
码嘛),那只可能出在编译器或者编译选项身上。由于我这里给出的是命令行选项,很容易发现问题所在。我当时遇到这个场景是在一个IDE环境中,费了好大的
劲才从一堆选项中把元凶揪出来:-P
其实,这堆错误信息是由编译选项/Za引起的。/Za选项表示禁用编译器扩展,而我们这里讨论的Checked Iterator刚好就是编译器扩展功能,so……over
阅读(1629) | 评论(0) | 转发(0) |