C++ 在运行时更改数组维度

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双数组。在构造函数中,可以指定宽度和高度。当宽度为1时,我希望创建1d阵列而不是2d阵列。因为我重载了[]运算符并返回了指针。如果只有1行/列,我不希望总是写入[I][0]。相反,我只想写[I]。 有人知道如何解决这个问题吗

编辑:
为了澄清这一点,我需要这个类来进行矩阵计算,而不仅仅是数组。

要制作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); }
}