在运行时更改数组维度

时间:2016-12-28 22:06:41

标签: c++ arrays pointers c++14

我有这个矩阵类,它有一个2d双数组。在构造函数中,您可以指定宽度和高度。我想在宽度为1时创建一个1d数组而不是2d。因为我重载了[]运算符并返回指针。如果只有1行/列,我不想总是写[i] [0]。相反,我想写[i]。 有谁知道如何解决这个问题?

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

5 个答案:

答案 0 :(得分:2)

您可以将两种替代类型包装为变体类型(标记的联合)。

但是,您无法使用operator[]来访问这两种变体,因为每种变体的返回类型都不同。一个会返回对子阵列的引用,而另一个会返回一个double。您不能有两个仅因返回类型而不同的重载。

相反,您可以使用重载函数。例如,double at(size_type x)double at(size_type x, size_type y)

然而,由于你代表一个矩阵,使用一维数组来表示任意等级的矩阵可能更简单,方法是将更高维度连续放置,就像多维数组存储在内存中一样(内存是一维的) , 毕竟)。这允许您在运行时指定每个维度的宽度,并避免变体类型的复杂性。

答案 1 :(得分:0)

如果您没有C ++ 17编译器,一个不错的解决方案是使用std::variantboost::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.
};

答案 2 :(得分:0)

这个问题对我很感兴趣,在我原来的答案中,我说过所谓的操作是不可能的,因为在C ++中你不能重载只有它们的返回类型不同的函数。

但是如果可以的话呢?

我变得很好奇,所以我花了很多时间研究和摆弄C ++,看看我是否可以让一个函数返回多种类型......答案就是&#39;是的,& #39;但这是一种不常用的方式,因为它使用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 */

main中,执行以下演示:

#include <iostream>
#include "Matrix.h"

int main() {
    //Give the type so the correct size_t is allocated in memory.
    Matrix <double> matrix1(1, 3);
    Matrix <double> matrix2(2,2);

    //Now have an array of void pointers.
    std::cout << "1 dimensional" << std::endl;
    for(int i=0;i<matrix1.getSize();++i){
        std::cout << matrix1[i] << std::endl;
    }

    //Cast the void *, then dereference it to store values.
    *((double*)matrix1[0]) = 77;
    *((double*)matrix1[1]) = 31;
    *((double*)matrix1[2]) = 24.1;

    for(int i=0;i<matrix1.getSize();++i){
        std::cout << *((double *)matrix1[i]) << std::endl;
    }

    std::cout << "2 dimensional addresses." << std::endl;
    for(int i=0;i<2;++i){
        double * _row = (double*)matrix2[i];
        for(int j=0;j<2;++j){
            std::cout << &_row[j] << " ";
        }
        std::cout << std::endl;
    }

    std::cout << "2 dimensional assignment and display." << std::endl;
    double num = 13.1;
    for(int i=0;i<2;++i){
        double * _row = (double*)matrix2[i];
        for(int j=0;j<2;++j){
            _row[j] = num;
            num += 0.13;
            std::cout << _row[j] << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

答案 3 :(得分:0)

我建议使用一维数组,user2079303 suggested in their answer。但是,如果这不可取,那么也可以用模板或多态实现你的想法。

如果您不需要动态更改其尺寸,则可以使用尺寸作为模板参数将矩阵设为模板类。反过来,这允许使用SFINAE将维度用于编译时逻辑,这允许您根据矩阵的维度重载operator[]()

请注意,在Rows == 1Cols == 1时,实际不创建一维数组,它只是模拟一个;如果您明确需要这种行为,而不是技术上不同但大多数是等效的行为,那么您将需要考虑在一个或两个参数为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{{enter image description here}} {}

    // 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;
    }
};

它可以用作......

// 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);
        }
    }
}

在行动here中查看。

如果你需要能够动态改变其尺寸,这就变得更加复杂。最简单的解决方案可能是使用多态,并让operator[]返回代理。

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);

然后,您可以让每个类生成所需的代理成员public,而不是private; private得到了一个非功能性的虚拟实现。

// 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';
        }
    }
};

由于引用可用于多态,Matrix可用作接口,允许任何不特别需要实际类之一的Matrix&

在行动here中查看。

通过这样的设置,通过提供辅助函数,可以轻松地动态创建正确的矩阵。

// 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); }
}

像往常一样,模板版本更冗长,但更安全(由于不必使用指针)并且可能更高效(由于不需要进行尽可能多的动态分配和解除分配;我还没有测试过但是,这个。

答案 4 :(得分:-1)

要制作2D动态数组,您需要使用一些数据结构概念,我正在考虑您不熟悉它们。在此之前,让我向您展示如何制作一维动态阵列。

int main()
{
  int size;

  std::cin >> size;

  int *array = new int[size];

  delete [] array;

  return 0;
}

请勿忘记删除您使用new分配的每个数组。

让我们回到2D动态数组。这些数组可以概括为散列表,这是一种常见的数据结构类型。现在这个人已经比我更好地解释了2D阵列How to create dynamic 2D array。点击链接。