【问题标题】:Cast (C like) pointer to C++ matrix将(C 类)指针转换为 C++ 矩阵
【发布时间】:2018-11-18 07:49:48
【问题描述】:

我想将矩阵从一个函数传递到另一个函数,所以我这样做了:

#include <cstdlib>
using namespace std;

const int N = 2;

void init_matrix(int (*matrix)[N]) 
{
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1;
}

int main() 
{
    int *x = (int*)malloc(N*N*sizeof(int));
    init_matrix( (int (*)[N]) x );
    return 0;
}

如何更轻松地拨打init_matrix()?我把它想象成init_matrix(x, N)。

请注意,我有点避免std::vector,因为我很快就会使用并行编程(pthreads、OpenMP、MPI)。 所以我会要求一个没有std 方法来构建矩阵的解决方案。

【问题讨论】:

  • 不要在 C++ 中使用 malloc、using namespace std 和(C 风格)数组。在此期间,也不要使用new。如果你真的需要一个矩阵而不是consider 创建你自己的矩阵。
  • 最好以std::vector 或std::array 为基础,而不是使用指针或new
  • 首先决定你打算使用C还是C++。 C++ 标准库提供了 C 没有的替代方案。两种语言都没有“标准矩阵”。
  • std::vector 在使用多线程库时不亚于手动分配内存。
  • @KcFnMi - 那么没有选择。 C 和 C++ 都没有一个通用选项可以让您避免代码中的类型转换。更不用说,如果您坚持使用malloc() - 当使用malloc() 时,C 中的好的做法是 C++ 中的坏做法,反之亦然。因此,您实质上是在寻求一种在两种语言中都可能技术不佳的解决方案。无论您使用哪种语言,并行编程都需要扎实的技术。

标签: c++ matrix multidimensional-array c++14 raw-pointer


【解决方案1】:

别这样。

多维数组并不是 C++ 最擅长的地方。在这里它仍然是可以接受的,因为 N 是一个编译时间常数,但如果它是一个运行时变量,你会被关闭,因为标准 C++ 不支持 VLA(即使它们被支持作为实现扩展 通过 gcc 和 clang 等一些实现。

因此,如果您确实需要处理真正的 2D 数组,只需使用包含底层 std::array 的自定义类(用于固定编译时间维度)或用于运行时维度的向量(用于总大小),并为其提供 2d 访问器。由于std::arrays 和向量是对象,因此您可以避免资源复制和移动噩梦(*)。

一个简化的实现可能是:

class matrix {
   std::vector<int> vec;
   int  rows;
   int cols;

public:
   matrix(int i, int j): rows(i), cols(j), vec(i * j) {}

   int& at(int i, int j) {
       return vec[j + i * cols];
   }

   const int& at(int i, int j) const {
       return vec[j + i * cols];
   }
};

这样,底层结构仍然是一个真正的二维数组,并且您确实有方法在其二维中使用它

这里还缺少什么:

  • 索引和大小应该是size_t 而不是int
  • 可以在at 方法中测试索引是否在可接受的范围内 - 如果您不检查它们,则不应调用该函数at...
  • 其他构造函数在现有二维数组之上构建矩阵(例如,可能来自 C 遗留代码)

补充说明:

你说你想避免可能的多线程的向量。首先,我无法想象为什么向量的多线程安全性不如手动分配的动态数组。其次,如果您确实需要手动分配,则必须遵循三/五规则,并在自定义析构函数之外实现自定义复制/移动构造函数和赋值运算符。


(*) 我的项目是编写一个支持operator [] 和迭代器的通用多维容器,所以我知道这是一项相当复杂的任务。我已经向code review 提交了一个预版本,但它仍然远非简单可用。

【讨论】:

  • 你的函数和构造函数是私有的......我认为调用函数at 而不实现范围检查是很危险的,因为这是为 STL 容器定义的。就像描述的here一样使用operator()。
  • @JHBonarius:看来我忘记了public: 关键字...非常感谢您的关注
  • @KcFnMi: 如果m 是矩阵的一个实例,&amp;m.at(0,0) 将是全局数组的第一个元素——它只是一个int* 而不是void*。跨度>
  • 这个答案是否类似于“@Ron 发布的链接”但使用std::vector 而不是new/delete?
  • 是的,但由于它使用底层容器,它可以使用默认析构函数,以及复制/移动构造函数和赋值运算符。由于资源管理问题(三规则,五规则),使用新删除需要为所有这些自定义(或已删除)版本
【解决方案2】:

您可以使用别名使调用看起来更好:

#include <cstdlib>
#include <iostream>
using namespace std;

const int N = 2;

using TypeMatrix =int (*)[N]; //alias

void init_matrix(TypeMatrix matrix) { //simply use the alias
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1;
}


int main() {
    TypeMatrix x = (TypeMatrix)(int*)malloc(N*N*sizeof(int)); //avoid malloc
    init_matrix(x);
   }

另请注意,您可以使用模板别名将其提升到一个新的水平,如下所示:

#include <cstdlib>
#include <iostream>
using namespace std;

const int N = 2;

template<typename T , int K>
using TypeMatrix =T (*)[K];

using MatrixInt =TypeMatrix<int,N>;
using MatrixDouble =TypeMatrix<double,N>;


template <typename Matrix>
void init_matrix(Matrix matrix) {
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1.1;
}


template <typename Matrix>
void print(Matrix matrix) {
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            std::cout<<matrix[i][j]<< " ";
}


int main() {
    MatrixInt x = (MatrixInt) (new int[N*N]);
    MatrixDouble y = (MatrixDouble) (new double[N*N]);
    init_matrix(x);
    init_matrix(y);
    print(x);
    print(y);
}

也就是说,我非常不鼓励使用原始指针和混合 C 和 C++(malloc 与 new)来完成此类工作。请改用std::array 或std::vector。

【讨论】:

  • 仍在使用 malloc... 为什么?
  • 但是现在你有内存泄漏
  • “线性化”这就是我从 malloc 中得到的,不是吗?
  • @JHBonarius 我只是想回答这个问题,而不是在代码中找到所有问题。泄漏从一开始就存在。随意编辑 =)
  • @JHBonarius 但现在你有内存泄漏:从某种角度来看,我认为这让每个人都不要使用原始指针(至少对我来说)。尽管有这个问题,我还是喜欢这个解决方案。
【解决方案3】:

忽略在 C++ 中使用 malloc 的不良做法,然后...

您可以将函数参数中的数组指针替换为数组。这将衰减回数组指针,但它使函数接口更清晰。

在 C 和 C++ 中都不能在 int(*)[N] 和 (int*) 之间进行转换。它们不是兼容的指针类型。即使它们指向同一个地址,您也会遇到由严格的指针别名导致的未定义行为问题。

您可以像这样重写代码,但也不建议这样做:

#include <cstdlib>
using namespace std;

const int N = 2;

void init_matrix(int matrix[N][N]) {
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1;
}

int main() {
    int (*x)[N] = (int(*)[N])malloc(N*N*sizeof(int));
    init_matrix(*x);
}

我不建议在 C++ 中使用它 - 如果没有 C 的 VLA 功能,这种风格会很麻烦。在 C++ 中,使用 std::array&amp; 可能更明智。

【讨论】:

    【解决方案4】:

    如果出于某种原因需要动态分配连续整数,可以使用数组向量或智能指针。

    std::vector<int[N]> x(N);
    std::unique_ptr<int[][N]> x = std::make_unique<int[][N]>(N);
    

    使用您的init_matrix 实现,您可以简单地传入第一个元素的地址。

    init_matrix(&x[0]);
    

    但是,最好通过引用传递向量或智能指针。

    void init_matrix (std::vector<int[N]> &matrix) {
        ...
    }
    
    void init_matrix (std::unique_ptr<int[][N]> &matrix) {
        ...
    }
    
    ...
    init_matrix(x);
    

    【讨论】:

    • *(int (*)[N][N])&amp;x[0] 我不确定你在这里找到正确的类型?指向数组int [N] 的指针与指向数组int [N][N] 的指针不兼容。与数组不同,数组指针不会衰减。 int (**)[N] 与 int (*)[N][N] 和 int (*)[N] 不兼容。
    • 这是一个重新解释的演员表,所以&amp;x[0] 不是int (*)[N][N]。强制转换将其强制为这样的指针,然后取消引用它以获取要传递给函数的数组类型。
    • 强制转换和取消引用违反了严格的别名规则。
    • @aschepler:不清楚,因为应该允许在表达式中转换后的数组生成的指针值以某种方式转换回原始数组。
    • @jxh vector 实现不太可能创建 int[N][N] 对象。更有可能是 unsigned char[N*N*sizeof(int)] 对象或类似对象,然后是同一存储中的多个单独的 int[N] 对象。
    【解决方案5】:

    使用std::vector 进行多线程编程并没有错,例如解释here。任何其他存储都同样危险。

    除了其他答案中显示的所有向量示例:如果您使用的是固定大小的矩阵,您甚至可以考虑使用基于 std::array 的模板。

    #include <iostream>
    #include <array>
    
    template <class T, size_t rows, size_t cols>
    class Matrix {
    public:
        Matrix() {}
        Matrix(T init) { data.fill(init); }
        double& operator() (size_t row, size_t col)
        {
            if (row < rows && col < cols) return data[cols*row + col];
            throw std::out_of_range("Matrix subscript out of bounds.");
        }
        double operator() (size_t row, size_t col) const
        {
            return data(row, col);
        }
    private:
        std::array<T, rows*cols> data;
    };
    
    int main()
    {
        Matrix<double, 2, 2> mat(1.0);
        mat(1,1) = 2.3;
        std::cout << "mat(1,1) = " << mat(1,1) << std::endl;
    }
    

    附言示例中实现的大小检查无效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-05
      • 2016-08-07
      • 2021-09-20
      • 1970-01-01
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      相关资源
      最近更新 更多