C++ 在运行时更改数组维度
我有一个矩阵类,它有一个2d双数组。在构造函数中,可以指定宽度和高度。当宽度为1时,我希望创建1d阵列而不是2d阵列。因为我重载了[]运算符并返回了指针。如果只有1行/列,我不希望总是写入[I][0]。相反,我只想写[I]。 有人知道如何解决这个问题吗 编辑:C++ 在运行时更改数组维度,c++,arrays,pointers,c++14,C++,Arrays,Pointers,C++14,我有一个矩阵类,它有一个2d双数组。在构造函数中,可以指定宽度和高度。当宽度为1时,我希望创建1d阵列而不是2d阵列。因为我重载了[]运算符并返回了指针。如果只有1行/列,我不希望总是写入[I][0]。相反,我只想写[I]。 有人知道如何解决这个问题吗 编辑: 为了澄清这一点,我需要这个类来进行矩阵计算,而不仅仅是数组。要制作2D动态数组,您需要使用一些数据结构概念,我认为您不熟悉这些概念。在此之前,让我向您展示如何创建一维动态数组 int main() { int size; std
为了澄清这一点,我需要这个类来进行矩阵计算,而不仅仅是数组。要制作2D动态数组,您需要使用一些数据结构概念,我认为您不熟悉这些概念。在此之前,让我向您展示如何创建一维动态数组
int main()
{
int size;
std::cin >> size;
int *array = new int[size];
delete [] array;
return 0;
}
不要忘记删除您分配的每个新数组
让我们回到二维动态阵列。这些数组可以概括为哈希表,这是一种常见的数据结构类型。现在,这个人比我更好地解释了2D数组。请按照链接进行操作。您可以将这两种可选类型包装成一种变体类型(标记的联合) 但是,不能使用
运算符[]
访问这两个变量,因为每个变量的返回类型都不同。一个将返回对子数组的引用,而另一个将返回一个双精度数组。不能有两个只因返回类型不同而不同的重载
相反,您可以使用重载函数。例如,double at(尺寸类型x)
和double at(尺寸类型x,尺寸类型y)
但是,由于您要表示一个矩阵,使用一维数组来表示任意秩的矩阵可能会更简单,方法是将高维连续放置在平面上,就像多维数组存储在内存中一样(毕竟内存是一维的)。这允许您在运行时指定每个维度的宽度,并避免变量类型的复杂性。一个好的解决方案是使用
std::variant
,或者如果您没有C++17编译器,则使用boost::variant
。我将创建一个易于使用的容器
template<typename T>
struct DynamicDimension {
std::variant<T, std::vector<T>> element;
// accessors, is_vector and is_element function.
// Maybe operator[] and push_back and a get function.
// Add begin and end to make your class useable with range for loops.
};
模板
结构动态维度{
std::变异元素;
//访问器,is_向量和is_元素函数。
//可能是运算符[]和push_back以及get函数。
//添加begin和end以使类可用于循环的范围。
};
这个问题对我很有兴趣,在我的最初回答中,我说不可能的操作是因为C++中不能重载只有返回类型不同的函数。p>
但是如果你可以呢
<>我变得好奇,所以我花了很多时间研究和篡改C++,看看我是否能使一个函数返回多种类型…答案是“是的”,但这是一种不常用的方法,因为它使用了void*
,这使生活变得困难
然而,它确实增加了编写更少代码行的额外优势,即使代码更难理解
也就是说,我从这次经历中学到了一些东西,所以我想我应该与大家分享
因此,有不同的方法可以用更少的代码来解决这个问题。在矩阵类中,有两个指针,一个是T*matrix
,另一个是T**matrix2d
,并使用void*
作为重载的[]
运算符的返回类型
#ifndef matrix_h
#define matrix_h
template <typename T>
class Matrix {
private:
int width;
int height;
int size;
T * matrix;
T ** matrix2d;
public:
Matrix(const int w, const int h): width(w), height(h){
if(w==1){
matrix = new T[h];
size = h;
} else if (h==1){
matrix = new T[w];
size = w;
} else {
matrix2d = new T*[h];
for(int i=0;i<h;++i){
matrix2d[i] = new T[w];
}
size = w*h;
}
}
~Matrix() {
if(width==1 || height==1){
delete [] matrix;
} else {
for(int i=0;i<height;++i){
T * _r = matrix2d[i];
delete [] _r;
}
delete [] matrix2d;
}
}
void * operator[](const int i){
if(width==1 || height==1){
return & matrix[i];
} else {
return & (*matrix2d[i]);
}
}
const int getSize(){
return size;
}
};
#endif /* matrix_h */
我建议使用一维数组,如图所示。但是,如果这不可取,也可以使用模板或多态性来实现您的想法
如果不需要动态更改其维度,可以将矩阵作为模板类,使用其维度作为模板参数。这反过来又允许使用SFINAE将维度用于编译时逻辑,这允许您基于矩阵的维度重载运算符[]()
注意,当Rows==1
或Cols==1
时,这实际上并不创建1D数组,它只是模拟一个数组;如果您明确需要该行为,而不是技术上不同但基本相同的行为,那么您将需要研究在一个或两个参数都为1
时对类进行专门化。这可能需要复制至少部分代码,因此会有点混乱
// This example uses std::array for the actual array, since it's compile-time anyways.
// This, in turn, lets it declare a lot of functions constexpr.
// Const-correctness omitted for brevity. Remember to restore it for actual code.
template<size_t Rows, size_t Cols>
class Matrix {
std::array<std::array<double, Cols>, Rows> mat;
public:
// Default constructor. Clang _really_ likes braced initialiser lists.
constexpr Matrix() : mat{{{{0}}}} {}
// Array constructor.
constexpr Matrix(const decltype(mat)& arr) : mat(arr) {}
// -----
// Subscript operators.
// Generic operator. Matrix<x, y>, where x != 1 && y != 1.
// Does what normal subscript operators do.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<!R && !C, size_t> i) {
return mat[i];
}
// Magic operator. Matrix<1, x>, where x != 1.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<(R && !C), size_t> i) {
return mat[0][i];
}
// Magic operator. Matrix<x, 1>, where x != 1.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<C && !R, size_t> i) {
return mat[i][0];
}
// Scalar matrix operator. Matrix<1, 1>.
// Just returns mat[0][0], for simplicity's sake. Might want to make it do something
// more complex in your actual class.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<R && C, size_t> i) {
return mat[0][0];
}
// -----
// A few interface helpers.
// Simple begin() & end(), for example's sake.
// A better version would begin at mat[0][0] and end at mat[Rows - 1][Cols - 1].
constexpr auto begin() const { return mat.begin(); }
constexpr auto end() const { return mat.end(); }
// Generic helpers.
constexpr size_t size() const { return mat.size() * mat[0].size(); }
constexpr size_t rows() const { return mat.size(); }
constexpr size_t cols() const { return mat[0].size(); }
// 1D Matrix helpers.
constexpr bool is_one_d() const { return (Rows == 1) || (Cols == 1); }
constexpr bool one_row() const { return Rows == 1; }
constexpr size_t dimension() const { return (one_row() ? cols() : rows()); }
// -----
// Output.
// Would need modification if better begin() & end() are implemented.
friend std::ostream& operator<<(std::ostream& str, const Matrix<Rows, Cols>& m) {
for (auto& row : m) {
for (auto& elem : row) {
str << std::setw(6) << elem << ' ';
}
str << '\n';
}
str << std::endl;
return str;
}
};
然后,您可以让每个类将它需要的代理成员设置为public
,而不需要的代理成员设置为private;private
one得到一个非功能性的虚拟实现
// Resizing omitted for brevity.
class OneDMatrix : public Matrix {
double arr[5];
// Proxy for single element.
class OneDProxy : public SubscriptProxy {
double& elem;
operator double*() override { return &elem; }
double& operator[](size_t) override { return elem; }
public:
OneDProxy(double& e) : elem(e) {}
operator double&() override { return elem; }
double& operator=(double d) override {
elem = d;
return elem;
}
};
public:
OneDMatrix() : arr{0} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static OneDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new OneDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 5; i++) {
str << std::setw(4) << arr[i] << ' ';
}
str << std::endl;
}
};
// Resizing omitted for brevity.
class TwoDMatrix : public Matrix {
double arr[3][4];
// Proxy for array.
class TwoDProxy : public SubscriptProxy {
double* elem;
operator double&() override { return elem[0]; }
double& operator=(double) override { return elem[0]; }
public:
TwoDProxy(double* e) : elem(e) {}
operator double*() override { return elem; }
double& operator[](size_t i) override { return elem[i]; }
};
public:
TwoDMatrix() : arr{{0}} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static TwoDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new TwoDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 3; i++) {
for (size_t j = 0; j < 4; j++) {
str << std::setw(4) << arr[i][j] << ' ';
}
str << '\n';
}
}
};
与往常一样,模板版本更详细,但更安全(因为不必使用指针),而且可能更高效(因为不需要进行太多的动态分配和解除分配;但是,我还没有对此进行测试)。如果矩阵大小是在运行时确定的,那么编译器就无法知道宽度是否为1。一种可能是在这种情况下使用不同的类。您需要创建第二个表示1D数组的类。更好的是,使用
std::vector您需要能够动态调整矩阵大小吗?如果没有,您可以将行和列作为模板参数,这将使您能够专门处理1
@JustinTime No的情况。您至少可以使用unique\u ptr
。它并没有回答这个问题。
// Get rid of any "Waah, you didn't use that!" warnings.
// See https://stackoverflow.com/a/31654792/5386374
#define UNUSED(x) [&x]{}()
// This should really use if constexpr, but online compilers don't really support it yet.
// Instead, have an SFINAE dummy.
template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<(Rows == 1) || (Cols == 1), int> dummy = 0) {
UNUSED(dummy);
//for (size_t i = 0; i < (m.one_row() ? m.cols() : m.rows()); i++) {
for (size_t i = 0; i < m.dimension(); i++) {
m[i] = (i ? i : 0.5) * (i ? i : 0.5);
}
}
template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<!((Rows == 1) || (Cols == 1)), int> dummy = 0) {
UNUSED(dummy);
for (size_t i = 0; i < m.rows(); i++) {
for (size_t j = 0; j < m.cols(); j++) {
m[i][j] = (i ? i : 0.5) * (j ? j : 0.5) + (i >= j ? 0.1 : -0.2);
}
}
}
class Matrix {
protected:
// Out proxy class.
// Visible to children, for implementing.
struct SubscriptProxy {
virtual operator double&() = 0;
virtual operator double*() = 0;
virtual double& operator=(double) = 0;
virtual double& operator[](size_t) = 0;
virtual ~SubscriptProxy() = default;
};
public:
virtual SubscriptProxy& operator[](size_t i) = 0;
virtual ~Matrix() = default;
virtual void out(std::ostream& str) const = 0;
friend std::ostream& operator<<(std::ostream& str, const Matrix& m) {
m.out(str);
return str;
}
};
std::ostream& operator<<(std::ostream& str, const Matrix& m);
// Resizing omitted for brevity.
class OneDMatrix : public Matrix {
double arr[5];
// Proxy for single element.
class OneDProxy : public SubscriptProxy {
double& elem;
operator double*() override { return &elem; }
double& operator[](size_t) override { return elem; }
public:
OneDProxy(double& e) : elem(e) {}
operator double&() override { return elem; }
double& operator=(double d) override {
elem = d;
return elem;
}
};
public:
OneDMatrix() : arr{0} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static OneDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new OneDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 5; i++) {
str << std::setw(4) << arr[i] << ' ';
}
str << std::endl;
}
};
// Resizing omitted for brevity.
class TwoDMatrix : public Matrix {
double arr[3][4];
// Proxy for array.
class TwoDProxy : public SubscriptProxy {
double* elem;
operator double&() override { return elem[0]; }
double& operator=(double) override { return elem[0]; }
public:
TwoDProxy(double* e) : elem(e) {}
operator double*() override { return elem; }
double& operator[](size_t i) override { return elem[i]; }
};
public:
TwoDMatrix() : arr{{0}} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static TwoDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new TwoDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 3; i++) {
for (size_t j = 0; j < 4; j++) {
str << std::setw(4) << arr[i][j] << ' ';
}
str << '\n';
}
}
};
// Assume OneDMatrix is expanded for dynamic size specification.
// It now has constructor OneDMatrix(size_t sz), and owns a dynamic double[sz].
// Assume TwoDMatrix is expanded for dynamic size specification.
// It now has constructor TwoDMatrix(size_t r, size_t c), and owns a dynamic double[r][c].
Matrix* createMatrix(size_t rows, size_t cols) {
if (rows == 1) { return new OneDMatrix(cols); }
else if (cols == 1) { return new OneDMatrix(rows); }
else { return new TwoDMatrix(rows, cols); }
}