【问题标题】:vector/array bounds check only when a define is declared仅在声明定义时检查向量/数组边界
【发布时间】:2015-08-29 10:22:31
【问题描述】:

我创建了自己的容器,该容器继承自向量。我想重新实现operator[] 以检查由#define 决定的边界。

举个例子,忽略模板参数,因为它们复杂且不相关

class MyArray : vector<double>
{
    //...
    virtual double& operator[](const size_type& index);
    virtual const double& operator[](const size_type& index) const;
    //...
}


double& MyArray::operator[](const size_type& index)
{
    #ifdef DEBUG_ENABLED
        return this->at(index);
    #else
        return (*this)[index];
    #endif
}

但是,这不起作用,因为由于 operator[] 已重载,因此在 #else 处调用 operator[] 将变得递归。

我想根据我的#define 来检查边界,而不是根据我使用std::vector&lt;&gt;::at() 还是std::vector&lt;&gt;::operator[]

我该如何解决这个问题?

编辑:因为它提供了很多使用 std::vector 作为成员而不是继承,我不得不提到这样做对我来说不是一个好的解决方案,因为我必须重新实现所有的成员函数标准::向量。这样做可不是那么愉快!

【问题讨论】:

  • std::vector 的常见实现已经有了这个,所以你可以保持 std::vector::operator[] 不变。例如见here
  • std::vector&lt;double&gt; 开始实习而不是继承。
  • return vector&lt;double&gt;::at(index);代替return (*this)[index];
  • @πάνταῥεῖ 我会这样做,但我不喜欢那样,因为我必须重写 std::vector 的所有函数。
  • @TheQuantumPhysicist I tried this now and it crashed my program too, 为什么你的程序崩溃了?这不可能是递归问题,因为在注释中给您的调用调用了基类运算符 [ ]。见这里:coliru.stacked-crooked.com/a/2470e03aa0868f05 运行时错误不是由于堆栈溢出,而是由于索引超出范围。

标签: c++ class operator-overloading operators element


【解决方案1】:

只需根据需要调用基类成员函数:

double& operator[](size_type index)
{
    #ifdef DEBUG_ENABLED
        return std::vector<double>::at(index);
    #else
        return std::vector<double>::operator[](index);
    #endif
}

您也应该提供一个const 版本。另请注意,您可能不想将此运算符设为virtual

【讨论】:

    【解决方案2】:

    首先,从标准容器派生不是一个好主意,因为它们缺少适当支持用作基类的功能(例如虚拟析构函数)。标准容器的目的是它们不会被用作基础。

    如果你必须这样做,添加这个;

    class MyArray : vector<double>
    {
        //   all your other stuff
    
        typedef vector<double> Base;
    
    };
    
    double& MyArray::operator[](const size_type& index)
    {
        #ifdef DEBUG_ENABLED
            return this->at(index);
        #else
            return (*((Base *)this))[index];
        #endif
    }
    

    通常认为使用static_cast 可以更好地完成函数中的类型转换,但您明白了。

    最好让容器成为你的类的成员,操作符的各种成员函数都转发给被包含的成员。

    class MyArray
    {
        //   all your other stuff
    
       private:
           std::vector<double> data;
    
    
    };
    
    double& MyArray::operator[](const size_type& index)
    {
        #ifdef DEBUG_ENABLED
            return data.at(index);
        #else
            return data[index];
        #endif
    }
    

    最后,无论哪种方式,operator[]() 不必是虚拟的。

    【讨论】:

    • 为什么这里没有虚拟析构函数很重要?
    • 设计或打算派生自的类通常具有许多属性。缺少这些属性通常表明该类不是要派生自该类,或者实现者没有考虑这种可能性。虚拟析构函数就是这些属性之一。
    • 并非如此。私有继承通常不需要虚拟析构函数。
    【解决方案3】:

    “由于它提供了很多使用 std::vector 作为成员而不是继承的功能,我不得不提到这样做对我来说不是一个好的解决方案,因为我必须重新实现 std 的所有成员函数::vector。那样做可不是那么愉快!

    您不必这样做。我在这里使用继承来减轻压力。

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    template <typename T>
    class Vector_ : private vector<T>
    {
    public:
        virtual ~Vector_(){}
        virtual const T& operator[](const size_t index);
        virtual const T& at(const size_t index);
    };
    
    /*
    <<I would like to make the check for bounds based on my #define, and  not based on
    std::vector<>::at() or std::vector<>::operator[]. >>
    */
    template <typename T>
    const T& Vector_<T>::operator[](const size_t index)
    {
    #ifdef DEBUG_ENABLED
        return (*((vector<T>*)this))->at(index)
    #else
        return (*((vector<T> *)this))[index];
    #endif
    }   
    template <typename T>
    const T& Vector_<T>::at(const size_t index)
    {
    #ifdef DEBUG_ENABLED
        return (*((vector<T>*)this))->at(index)
    #else
        return (*((vector<T>*)this))[index];
    #endif
    }
    
    //test...
    Vector<int>_ vec;
    vec.push_back(3);
    
    vec[2]; or vec.at(2); //no exception
    
    #define DEBUG_ENABLED
    vec[2]; or vec.at(2);  //exception
    

    【讨论】:

    • OP 已经在使用继承。但他们是明智的并使用私有继承。标准库容器的公共继承不是一个好主意。此外,以前的答案中已经有可行的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-13
    • 1970-01-01
    • 2019-10-17
    • 2022-01-02
    • 1970-01-01
    相关资源
    最近更新 更多