VC 之 Checked Iterator

Checked Iterator是指具有越界检查功能的迭代器,并且会在检查到越界操作时触发运行时错误处理(调用非法参数处理例程或者抛出异常)。VC从VS2005 开始支持Checked Iterator。另外,VC还支持Debug Iterator,有更多的检查功能,这里不予讨论。

Part One 编译

    Checked Iterator能够确保迭代器不会发生越界访问(如果发生越界访问,则会进行相应的处理)。如果在需要Checked Iterator的地方使用了非Checked Iterator,则在编译时会产生编译警告C4996(Level 3)。如果需要禁用此警告,可以通过定义宏_SCL_SECURE_NO_WARNINGS来达到目的。

  1. #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才起作用)

示例代码如下:

  1. // cl /EHsc test1.cpp
  2. #include <vector>
  3. #include <iostream>
  4. using namespace std;
  5. void myInvalidParameterHandler(const wchar_t* expression,
  6.                                const wchar_t* function,
  7.                                const wchar_t* file,
  8.                                unsigned int line,
  9.                                uintptr_t pReserved)
  10. {
  11.     cout << "just ignore it" << endl;
  12. }
  13. int main()
  14. {
  15.     vector<int> v;
  16.     v.push_back(11);
  17.     v.push_back(22);
  18.     cout << v[1] << endl;
  19.     _set_invalid_parameter_handler(myInvalidParameterHandler);
  20.     cout << v[2] << endl;   // out of range!
  21.     return 0;
  22. }
复制代码

程序运行结果:

22
just ignore it
131074

另外,如果定义_SECURE_SCL_THROWS为1,则在Debug版程序中需要在捕获异常之前先调用 _CrtSetReportMode(_CRT_ASSERT, 0)禁用ASSERT报错,并在捕获异常之后恢复。因为在程序抛出异常之前会先触发断言失败。示例代码如下:

  1. // cl /EHsc /D_DEBUG /MDd test2.cpp
  2. #define _SECURE_SCL_THROWS 1
  3. #include <vector>
  4. #include <iostream>
  5. #include <crtdbg.h>  // For _CrtSetReportMode
  6. using namespace std;
  7. int main()
  8. {
  9.     vector<int> v;
  10.     v.push_back(11);
  11.     v.push_back(22);
  12.     cout << v[1] << endl;
  13.     // disable ASSERT
  14.     int saved = _CrtSetReportMode(_CRT_ASSERT, 0);
  15.     try {
  16.         cout << v[2] << endl;      // out of range
  17.     }
  18.     catch (std::out_of_range &) {
  19.         cout << "out of range" << endl;
  20.     }
  21.     // restore ASSERT
  22.     _CrtSetReportMode(_CRT_ASSERT, saved);
  23.     return 0;
  24. }
复制代码

程序运行结果:

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:

  1. // 示例代码片断
  2. int a[]={1, 2, 3, 4, 5, 6};
  3. int b[6];
  4. copy(a, a + 6, stdext::checked_array_iterator<int*>(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。


其它编译问题

    请尝试编译如下代码(注意使用下面提供的编译选项!):

  1. // 示例代码
  2. // cl /EHsc /D_DEBUG /MDd /Za test.cpp
  3. #define _SECURE_SCL_THROWS 1
  4. #include <string>
  5. int main()
  6. {
  7.     return 0;
  8. }
复制代码

我的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<char>,
            _Ax=std::allocator<char>
        ]
        D:/Microsoft Visual Studio 9.0/VC/INCLUDE/xstring(2221): 参见对正在编译
的类 模板 实例化“std::basic_string<_Elem,_Traits,_Ax>”的引用
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>,
            _Ax=std::allocator<char>
        ]

看到上面的源代码和报错信息,估计有人会发疯了(我无意中挖掘出这个编译场景,抓狂了半天!)。确实,代码本身是没有任意问题的(根本就没有一行自己的代码嘛),那只可能出在编译器或者编译选项身上。由于我这里给出的是命令行选项,很容易发现问题所在。我当时遇到这个场景是在一个IDE环境中,费了好大的劲才从一堆选项中把元凶揪出来:-P

    其实,这堆错误信息是由编译选项/Za引起的。/Za选项表示禁用编译器扩展,而我们这里讨论的Checked Iterator刚好就是编译器扩展功能,so……over

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值