二维网格 class C++ 实现

2D mesh class implementation in C++

我想实现一个内存效率高的 2D 网格 class,因为它将用于 Monte Carlo 模拟。然而,在这个阶段,这实际上与二维数组相同 class。不用说,我是 C++ 的新手。我写了下面的代码。

#define MESH2D_H_INCLUDED

class Mesh2D
{
private:

    double* mesh;
    size_t  rows;
    size_t  columns;

public:

    /* constructor */
    Mesh2D(size_t Nx, size_t Ny){
        rows    = Nx;
        columns = Ny;
        mesh    = new double[Nx*Ny] {};
        std::cout << "Mesh created." << std::endl;
    }

    /* destructor */
    ~Mesh2D()
    {
        delete[] mesh;
        std::cout << "Mesh deleted." << std::endl;
    }

    /* accessors */
    double getrows() const {return rows;}
    double getcolumns() const {return columns;}
    double& operator()(size_t i, size_t j)
    {
        if (i > rows || j > columns){
            throw std::out_of_range("Index exceeds array bounds.");
        }
        return mesh[j + i*columns]; // row major access
    }

    /* copy constructor */
    Mesh2D& operator=(const Mesh2D& rhs) // what is the difference between this line and the following? inline operator=(const Mesh2D& rhs)
    {
        if (rhs.rows != rows || rhs.columns != columns){
            throw std::out_of_range("Assignment cannot be performed, mesh dimensions do not agree.");
        }
        mesh = new double [rows*columns];

//        I want to avoid using a for loop
//        for (int i=0; i<rows*columns; i++){
//            mesh[i] = rhs.mesh[i];
//        }
//      Use this instead
        memcpy(mesh, rhs.mesh, sizeof(rhs)); //however the output is not the expected.
        std::cout << "copied!" << std::endl;
    }

};

#endif // MESH2D_H_INCLUDED
//MAIN FUNCTION

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

void PrintMesh2D(Mesh2D &mesh){ //why isn't it going to work if I add const? I mean: void PrintMesh2D(const Mesh2D &mesh)

    for (int i=0; i<mesh.getrows(); i++){

        for (int j=0; j<mesh.getcolumns(); j++){

            std::cout << mesh(i,j) << " ";
        }
        std::cout << std::endl;
    }
}

int main()
{

    Mesh2D mesh{3,3};
    Mesh2D newmesh{3,3};

    for (int i=0; i<mesh.getrows(); i++){
        for (int j=0; j<mesh.getcolumns(); j++){
            mesh(i,j) = j + i * mesh.getcolumns();
        }
    }

    newmesh = mesh;
    PrintMesh2D(newmesh);

}

我的问题写成评论,但我也把它们列在这里:

  1. 在拷贝构造函数里面,Mesh2D& operator=(const Mesh2D& rhs)这一行和inline operator=(const Mesh2D& rhs)这一行有什么区别?

  2. 同样,在复制构造函数中,我想避免 for 循环并改用 memcpy。但是,输出不是预期的,我做错了什么?

  3. 主要功能内部:为什么不编译? void PrintMesh2D(const Mesh2D &mesh)

  4. 最后,执行完成了吗?我的意思是,是否缺少任何重要的 features/functions?

欢迎您给我任何建议。由于我是 C++ 的新手,我感觉我正在编写错误的错误代码。

编辑:

我写了下面的作为拷贝构造函数。

/* copy constructor */
    Mesh2D (const Mesh2D& rhs)
    {
        rows    = rhs.rows;
        columns = rhs.columns;
        mesh    = new double[rows*columns];
        memcpy(mesh,rhs.mesh,rows*columns*sizeof(double));
        std::cout << "Copied!" << std::endl;
    } 

看起来不错?

  1. inline operator=(const Mesh2D& rhs) 是一个语法错误,它错过了 return 类型。另外 Mesh2D& operator=(const Mesh2D& rhs) 是复制赋值运算符,而不是复制构造函数,即 Mesh2D(const Mesh2D& rhs).
  2. 您可以使用 memcpy,但最后一个参数应该是正确的大小 rows*colums*sizeof(double)sizeof(rhs)是class对象的大小,与数组大小无关。由于您在复制赋值运算符内部而不是复制构造函数内部,因此 mesh 的内存已经分配,​​因此您应该从复制赋值运算符中删除 mesh = new double [rows*columns]; 。您还必须在那里执行 return *this; 以匹配 return 类型。
  3. 因为你需要 const 版本的仿函数:double operator()(size_t i, size_t j) const
  4. 阅读规则 3 或规则 5,如果你有构造函数和析构函数,你应该实现复制构造函数。
  5. 来自@PaulMcKenzie:使用 std::vector 不必执行 1、2 或 4。

这是一个完整的例子:https://ideone.com/seOSwN

我会尝试将缺失的部分添加到您的代码中:

  1. 拷贝构造函数

  2. 一个有效的赋值运算符。

在下面的代码后,我会评论:

#include <iostream>
#include <cstring>
#include <algorithm>

class Mesh2D
{
    private:
    size_t  rows;
    size_t  columns;
    double* mesh;

public:

    Mesh2D(size_t Nx, size_t Ny) : rows(Nx), columns(Ny), mesh(new double[Nx*Ny]{})
    {
        std::cout << "Mesh created." << std::endl;
    }

    Mesh2D(const Mesh2D& rhs) : rows(rhs.rows), columns(rhs.columns), 
                                mesh(new double[rhs.rows * rhs.columns])
    {
       memcpy(mesh, rhs.mesh, (rhs.rows * rhs.columns) * sizeof(double));

       // or better yet
       // std::copy(rhs.mesh, rhs.mesh + rhs.rows * rhs.columns, mesh);
    }
    
    Mesh2D& operator=(const Mesh2D& rhs)
    {
       if ( &rhs != this )
       {
          Mesh2D temp(rhs);
          std::swap(temp.rows, rows);
          std::swap(temp.columns, columns);
          std::swap(temp.mesh, mesh);
      }
      return *this;
    }
       
    ~Mesh2D()
    {
        delete[] mesh;
        std::cout << "Mesh deleted." << std::endl;
    }


    double getrows() const {return rows;}
    double getcolumns() const {return columns;}

    double& operator()(size_t i, size_t j)
    {
        if (i > rows || j > columns){
            throw std::out_of_range("Index exceeds array bounds.");
        }
        return mesh[j + i*columns]; // row major access
    }

    double& operator()(size_t i, size_t j) const
    {  
        if (i > rows || j > columns){
            throw std::out_of_range("Index exceeds array bounds.");
        }
        return mesh[j + i*columns]; // row major access
    }
};

首先,请注意使用成员初始化列表来初始化 Mesh2D class 的成员。这样做是个好习惯,而不是在构造函数体内赋值。


其次,注意赋值运算符。它使用 copy / swap idiom 安全干净地释放旧内存并制作副本。


第三,operator() 应该有一个 const 重载,否则你永远不能在这样的地方使用你的 class:

void foo(const Mesh2D& m)
{
   std::cout << m(0,0);
}

原因是 m 是一个 const 引用,因此您只能在其上调用 const 函数。因此,operator() 需要一个 const 版本,以及非常量版本。

此外,operator() 的非 const 版本也需要存在的原因是您可能想要这样做:

void foo2(Mesh2D& m)
{
   m(0,0) = 23;
}

如果m只有operator()const版本,由于operator()更改了对象的内部状态,上述操作无法完成。


另一个问题是您使用 memcpy 不正确。 memcpy 适用于字节数,而不是数组中的项目数。因此您需要乘以 sizeof(double),因为这是您正在复制的字节数。一种类型更安全的解决方案是使用 std::copy,它会根据项目数自动使用正确的复制功能。


最后,错误的“Mesh2D”测试已从赋值运算符中删除。原因是赋值运算符(和复制构造函数)工作是一回事,而且只有一件事——制作副本。

当您开始将业务逻辑引入复制赋值函数时,您冒着制作不是副本的副本,而是正在传入的对象的伪副本的风险。

这会导致 C++ 中一些最讨厌和最难发现的错误,这就是当您的复制赋值函数想要玩游戏而不是真正进行复制时。如果你的复制构造函数或赋值运算符根据传入的值做出太多决定和执行逻辑,你就会知道什么时候做错了。


为了完成,这里是 class 的相同版本,但使用 std::vector<double>。请注意,由于不需要用户定义的赋值运算符、复制构造函数或析构函数,代码要小得多:

#include <iostream>
#include <vector>

class Mesh2D
{
    private:
    size_t  rows;
    size_t  columns;
    std::vector<double> mesh;

public:

    Mesh2D(size_t Nx, size_t Ny) : rows(Nx), columns(Ny), mesh(Nx*Ny)
    {
        std::cout << "Mesh created." << std::endl;
    }

    double getrows() const {return rows;}
    double getcolumns() const {return columns;}

    double& operator()(size_t i, size_t j)
    {
        if (i > rows || j > columns){
            throw std::out_of_range("Index exceeds array bounds.");
        }
        return mesh[j + i*columns]; 
    }

    const double& operator()(size_t i, size_t j) const
    {  
        if (i > rows || j > columns)    {
            throw std::out_of_range("Index exceeds array bounds.");
        }
        return mesh[j + i*columns]; 
    }
};