Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1075204
  • 博文数量: 77
  • 博客积分: 11498
  • 博客等级: 上将
  • 技术积分: 1840
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-04 11:10
文章分类

全部博文(77)

文章存档

2011年(1)

2010年(16)

2009年(5)

2008年(55)

分类: C/C++

2008-08-27 21:45:24


    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

阅读(1641) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~