C++ C++;访问二维数组中的元素的步骤
我正在寻找二维数组指针的重载[]运算符 访问单元格元素 二维数组作为C++ C++;访问二维数组中的元素的步骤,c++,arrays,multidimensional-array,operator-overloading,C++,Arrays,Multidimensional Array,Operator Overloading,我正在寻找二维数组指针的重载[]运算符 访问单元格元素 二维数组作为int*arr传递给我的函数 我们可以通过*(arr+i*N+j)访问cell元素,其中N是列计数 i是行索引,j是列索引 但是我们可以像arr[i,j]或arr(i,j)那样编写它以提高可读性吗 使用一些宏或运算符重载 有什么建议吗?无法更改内置类型运算符的行为。如果要重载运算符,至少一个操作数必须是用户定义的类型。否 运算符重载要求至少一种参数类型为类/结构/联合。宏不能做到这一点 如果可能的话,最接近的方法是通过引用传递数
int*arr
传递给我的函数
我们可以通过*(arr+i*N+j)
访问cell元素,其中N
是列计数
i
是行索引,j
是列索引
但是我们可以像arr[i,j]
或arr(i,j)
那样编写它以提高可读性吗
使用一些宏或运算符重载
有什么建议吗?无法更改内置类型运算符的行为。如果要重载运算符,至少一个操作数必须是用户定义的类型。否 运算符重载要求至少一种参数类型为类/结构/联合。宏不能做到这一点 如果可能的话,最接近的方法是通过引用传递数组。例如:
template<std::size_t width, std::size_t height>
void doThings(int(& array)[width][height])
{
// access array[i][j];
}
或者你需要在这里解决潜在的问题。为什么一个2D数组首先要经过int*
这里要记住的重要一点是不要使事情过于复杂。- 您可以编写一个索引函数来隐藏公式
如果您的
是全局定义的,请编写N
和它一起使用int index(int i, int j) { return i * N + j; }
arr[index(i, j)]
- 或者,围绕指针编写一个包装器类。这个类的编写完全没有运行时开销。它可以有一个运算符来允许语法
其中,arr[i][j]
是包装类的一个实例。这样的包装可以这样定义:arr
如您所见,其思想是为外部维度重载class Array2DWrapper { int *ptr; public: Array2DWrapper(int *ptr) : ptr(ptr) {} int * operator[](int i) { return ptr + i*N; } }; // USAGE: void exampleFunction(int *arrPtr) { Array2DWrapper arr { arrPtr }; ... arr[i][j]; ... }
,从而返回指向内部维度的指针。当这个类的用户写入操作符[]
时,它调用自定义操作符重载,它返回一个arr[i]
,然后下一个int*
使用内置的[j]
作为指针访问元素 请注意,上面的类可以用作函数参数,但调用方可以使用指向某个2D数组的原始指针调用它。这将自动调用此类的构造函数(“隐式转换”) 如果未全局定义操作符[]
,则应将其作为成员添加到类以及构造函数中N
但是现在,隐式转换不再起作用了class Array2DWrapperDynamicN { int *ptr; int N; public: Array2DWrapper(int *ptr, int N) : ptr(ptr), N(N) {} int * operator[](int i) { return ptr + i*N; } };
// USAGE: void exampleFunction(int *arrPtr, int N) { Array2DWrapperDynamicN arr { arrPtr, N }; ... arr[i][j]; ... }
- 你不能直接这么做。
我建议编写一个小类/结构来方便地包装2D数组。这里我使用
std::vector
而不是int*arr
,您不必关心内存管理。以下代码显示了3种可能的方法:
方法1(推荐):通过mat(i,j)
操作符()(size\u ti,size\t j)
被称为函数调用操作符
template<typename T>
struct Matrix
{
Matrix(const std::vector<T>& d, size_t s) : Matrix(d, s, s) {}
Matrix(const std::vector<T>& d, size_t c, size_t r) : data_(d), cols_(c), rows_(r) {}
std::vector<T> data_;
size_t cols_;
size_t rows_;
const T& operator()(size_t i, size_t j) const { return data_[i * cols_ + j]; }
T& operator()(size_t i, size_t j) { return data_[i * cols_ + j]; }
// and you can add other convenient methods
};
方法2:通过mat[{i,j}]
使用数组下标操作符只需要一个参数,因此您可以更改/添加以下操作符到类中:
const T& operator[](const std::array<size_t,2>& a) const { return data_[a[0] * cols_ + a[1]]; }
T& operator[](const std::array<size_t,2>& a) { return data_[a[0] * cols_ + a[1]]; }
像这样使用它:
Matrix<int> mat({1, 2, 3, 4, 5, 6, 7, 8, 9}, 3, 3); // a 3x3 matrix
std::cout << mat(1,2) << std::endl;
std::cout << mat[Idx(1),Idx(2)] << std::endl;
std::难道我认为写arr[I,j]比写(arr+iN+j)更易读,后者更容易出错和不可读。建议你读第二个选项。“但是我们必须在课堂上的某个地方加上N?”维姬我把答案扩大了一点。我不知道在您的情况下,N
是全局定义的,还是需要放在包装器中。这取决于代码的其余部分。。。N
来自哪里?它是一个函数参数吗?还是函数之外?你的隐式转换非常好。但如果N可以参与隐式转换,那就太好了,因为N不是全局定义的。如果能看到普通指针转换成具有2d索引功能的智能指针,那就太好了。@vicky但要做到这一点,它需要以某种方式知道内部尺寸的大小。。。在C++11中,可以使用初始值设定项列表对多个参数构造函数进行隐式转换。这有点复杂。基本上,您可以这样调用函数:exampleFunction({arr,N})
,请参见{arr,N}
是exampleFunction
的单个参数,如果函数具有签名exampleFunction(array2drapperdynamicn-arr),则会隐式转换为array2drapperdynamicn
通常情况下,行和列计数作为参数列表在2D数组后面传递,如exampleFunction(arr,m,n)。。。。在这种情况下我们能做些什么吗?这是什么类型的操作员?(我猜你希望第二个是非常量)这是操作符:mat(I,j)
。是的,很抱歉,当你编写代码时没有测试它;p:)关于带有两个参数的数组下标运算符:这确实只有在重载逗号运算符时才可能,但对于内置类型的两个参数,这同样是不可能的。通常的“变通方法”是使用语法[i][j]
(如我的回答中所示)。它还以更好的方式类似于原始二维数组的经典语法。但这当然是一种主观偏好。哦,对了,谢谢你的观点,逗号运算符重载也是不可能的。我会更仔细地看看你的答案!
template<typename T, size_t Cols, size_t Rows> struct Matrix
{
std::array<T, Cols * Rows> data_;
// etc...
const T& operator[](const std::array<size_t,2>& a) const { return data_[a[0] * cols_ + a[1]]; }
T& operator[](const std::array<size_t,2>& a) { return data_[a[0] * cols_ + a[1]]; }
struct Idx
{
Idx(size_t ii) : i(ii) {}
size_t i;
operator size_t() const { return i; } // implicit user-defined conversion
};
std::array<size_t, 2> operator , (Idx i1, Idx i2)
{
return { i1, i2 };
}
// etc...
// and we do not have to add Matrix operators since we reused the one from Method 2
std::cout << mat[Idx(1),Idx(2)] << std::endl;