在调试模式下使用 std::vector 和 std::array 检查运算符 [] 的范围

Range checking for operator[] with std::vector and std::array in debug mode

提问人:ander 提问时间:8/9/2023 最后编辑:LoSander 更新时间:11/11/2023 访问量:104

问:

我正在编写一些数字C++,其中两者都用于代码的性能关键部分。我最好在调试模式下进行范围检查,以清除任何潜在的越界访问,但在发布版本中不提供开销。std::vectorstd::arrayoperator[]

原则上,这可以通过断言语句轻松完成,但我不确定如何在不做坏事的情况下编辑 stl 库代码。我不会考虑使用该方法,因为这会在发布版本中产生开销。我正在使用 g++ 进行编译。我已经读到可以为 MSVC 编译器打开断言,但我使用的是 linux 和 g++,我认为这不是一个选项。at()

我已经为 创建了这个派生类,到目前为止,它对我的目的来说工作正常,但我怀疑这不是一个理想的解决方案。std::vector

template <typename T> class Vector final : public std::vector<T> 
{ 
public: 
    using std::vector<T>::vector; 
#ifndef NDEBUG 
    T &operator[](size_t i) 
    { 
        assert(i < this->size()); 
        return *(this->_M_impl._M_start + i); 
    } 
    const T &operator[](size_t i) const 
    { 
        assert(i < this->size()); 
        return *(this->_M_impl._M_start + i); 
    } 
#endif 
}; 

我无法为没有问题的人创建一个类似的派生类。这篇文章解决了以下问题:编译时触发了 std::vector 的范围检查,但似乎从 std 类继承并不流行。std::arraystd::vector

这篇文章的一个答案也建议使用标志,但我发现此检查从与工作正常的向量和数组无关的另一部分代码中产生了错误(尝试使用 yaml-cpp 库并使用 ).-D_GLIBCXX_DEBUGoperator[]

C++ 断言 stdarray 范围检查

评论

2赞 Jesper Juhl 8/9/2023
一个简单的选择:......将扩展到调试版本和发布版本。foo #if DEBUG .at(42); #else [42]; #endiffoo.at(42);foo[42];
0赞 doug 8/9/2023
适用于 Windows 可执行文件的 MSVC 会自动在调试模式下进行范围检查。您可以查看他们在源代码中所做的工作,看看它是否易于移植到其他操作系统。vector
2赞 HolyBlackCat 8/9/2023
-D_GLIBCXX_DEBUG,但我发现此检查从代码的另一部分产生了错误”然后那部分代码被窃听了,修复它!
0赞 ander 8/10/2023
@JesperJuhl我知道,但是如果这是你的意思,那么每次我访问向量时都必须写那个条件太不方便了。
0赞 ander 8/10/2023
@doug即使这是一个简单的解决方案,它对我有什么帮助?你建议我应该编辑矢量标题吗?似乎是个坏主意。但是,如果这是一个解决方案,我可以向向量和数组运算符[]方法添加一个断言

答:

0赞 user22361789 8/9/2023 #1

您可能需要:

template <typename T> 
class Vector final : std::vector<T> { ... };

int main() {
    Vector<int> v;

#ifndef NDEBUG
    v[1];    
#else 
    v.at(1);
#endif

    return 0;
}

或者修改类:Vector

template <typename T> 
class Vector final : std::vector<T> { 
public:
    using std::vector<T>::vector; 
 
    T &operator[](std::size_t i) { 
#ifndef NDEBUG 
        assert(i < this->size()); 
#endif
        return *(this->_M_impl._M_start + i); 
    } 

    const T &operator[](size_t i) const { 
#ifndef NDEBUG 
        assert(i < this->size()); 
#endif
        return *(this->_M_impl._M_start + i); 
    } 
};

评论

1赞 ander 8/10/2023
后一种解决方案基本上是我在帖子中展示的,但是我无法为 std::array 复制相同的解决方案
0赞 8/10/2023
@ander 对不起,冗余。我明白你的意思,但这是错误的,如果定义,那么你的类甚至不会有这些运算符重载NDEBUGVector