简体   繁体   English

如何在C ++中使用模板更有效地实现带有边界约束的2D数组?

[英]How can I implement a 2D array with bound cheking in C++ using template more efficiently?

Now I implement it like this: 现在,我像这样实现它:

template <typename T>
class Array2D{
  private:
    T **pointer = nullptr;
    int n1, n2;
    class tmp{
        private:
            T* pointer;
            int n;
        public:
            tmp(T* p, int n):pointer(p),n(n){}
            T& operator[](unsigned int x){
                assert(x<n);
                return pointer[x];
            }
    };
  public:
    Array2D(int n, int m):n1(n), n2(m){
        pointer = new T *[n];
        for (int i = 0; i < n; i++)
            pointer[i] = new T[m];
    }
    ~Array2D(){delete[] pointer;}
    tmp&& operator[](unsigned int x){
        assert(x < n1);
        return move(tmp(pointer[x], n2));
    }
};

I feel that the code a little bit ugly. 我觉得代码有点难看。 The reason why I use tmp class is that if it return T* directly in the class Array2D, it could not check the bounce in the second dimension. 我之所以使用tmp类的原因是,如果它直接在Array2D类中返回T *,则无法检查第二维中的反弹。 So I add a class named temporary. 因此,我添加了一个名为“临时”的类。 But I feel the effiency get worse for this class. 但是我觉得这门课的效率会变差。 Is there a better solution? 有更好的解决方案吗?

And another thing confuses me. 还有另一件事使我感到困惑。 I want others could use this class only in two dimension form. 我希望其他人只能以二维形式使用此类。 I mean that if you only use a [] to a instance of this class, you will get an error. 我的意思是,如果仅对此类的实例使用[],则会出现错误。 So I put class tmp in the private section. 因此,我将tmp类放在私有部分。 But I find it can not work. 但是我发现它不起作用。 Is there a solution? 有解决方案吗?

The existence of tmp is fine, any compiler will optimize these temporary instances away. tmp的存在很好,任何编译器都会优化这些临时实例。

Preventing the use of a single [] cannot be enforced, but you can trigger warnings by using [[nodiscard]] on the return value from Array2D::operator[] . 不能强制使用单个[] ,但是可以通过对Array2D::operator[]的返回值使用[[nodiscard]]来触发警告。

There is, however, a bug in your implementation: you're returning a dangling reference from your operator[] . 但是,实现中存在一个错误:您正在从operator[]返回一个悬空的引用。 Just do: 做就是了:

tmp operator[](unsigned int x){
    assert(x < n1);
    return tmp(pointer[x], n2);
}

Finally, you shouldn't go through the hassle of managing the storage's lifetime manually. 最后,您不应该手动管理存储生命周期的麻烦。 In fact, there's already several other bugs there: your destructor leaks memory, and your class doesn't obey the rule of 5, so copying or assigning Array2D 's will lead to UB. 实际上,这里已经存在其他几个错误:析构函数泄漏内存,并且您的类不遵守5的规则,因此复制或分配Array2D会导致UB。 Just use a std::vector<std::vector<T>> , or better yet (for contiguity) a single std::vector<T> which you split into rows. 只需使用std::vector<std::vector<T>> ,或者更好(出于连续性)将单个std::vector<T>分成几行即可。

Closing note: once your code works and is ready to be shown, you can take it for a walk at Code Review . 结束语:一旦代码可以使用并准备好显示,您可以在Code Review上散步。

One option would be not to use the subscript operator at all and just have an at function which takes both indexes. 一种选择是根本不使用下标运算符,而仅具有同时接收两个索引的at函数。 This would also be more consistent with the standard library containers which perform bounds checking in the at functions. 这也将与在at函数中执行边界检查的标准库容器更加一致。

For example: 例如:

template < typename T >
class Array2D
{
public:
    Array2D(size_t m, size_t n)
    : data( m, std::vector< T >( n, T() ) )
    {
    }

    T& at( size_t x, size_t y )
    {
        return data.at( x ).at( y );
    }

    const T& at( size_t x, size_t y ) const
    {
        return data.at( x ).at( y );
    }
private:
    std::vector<std::vector<T>> data;
};

Using std::vector to store your data gives: 使用std::vector来存储数据可以得到:

  1. fixes rule of 5 bugs 修复5个错误的规则
  2. manages all memory allocation for you 为您管理所有内存分配
  3. implements the bounds checking for you 为您实现边界检查

The less code you write and the more you rely on the standard library the fewer bugs you'll create (generally). 编写的代码越少,对标准库的依赖就越多,通常创建的错误就越少。

You can use the subscript operator with a non-integral type, for example if you use std::pair< size_t, size_t > you can implement it like this: 您可以将下标运算符与非整数类型一起使用,例如,如果您使用std::pair< size_t, size_t > ,则可以这样实现:

T& operator [] ( std::pair<size_t, size_t> p )
{
    return data.at( p.first ).at( p.second );
}

T& operator [] ( std::pair<size_t, size_t> p ) const
{
    return data.at( p.first ).at( p.second );
}

And call it like this: 并这样称呼它:

Array2D< int > a( 10, 20 );
a[{5,5}] = 10;
std::cout << a[{5,5}] << "\n";

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM