简体   繁体   中英

2D mesh class implementation in C++

I want to implement a 2D mesh class which is memory efficient, since it will be used in a Monte Carlo simulation. At this stage however, this is actually the same as a 2D array class. Needless to say, I am new to C++. I have written the following code.

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

}

控制台输出

My questions are written as comments but I am listing them here as well:

  1. Inside the copy constructor, what is the difference between this line Mesh2D& operator=(const Mesh2D& rhs) and this line inline operator=(const Mesh2D& rhs) ?

  2. Again, inside the copy constructor I would like to avoid a for loop and use memcpy instead. However, the output is not the expected, what am I doing wrong?

  3. Inside the main function: Why is this not going to compile? void PrintMesh2D(const Mesh2D &mesh)

  4. Finally, is the implementation complete? I mean, are there any important features/functions that are missing?

You are welcome to give me any piece of advice. Since I am new to C++, I have the feeling that I am writing bad buggy code.

EDIT:

I wrote the following as a copy constructor.

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

Looks right?

  1. inline operator=(const Mesh2D& rhs) is a syntax error, it misses the return type. Also Mesh2D& operator=(const Mesh2D& rhs) is the copy assignment operator, not the copy constructor, that would be Mesh2D(const Mesh2D& rhs) .
  2. You can use memcpy , but the last argument should be the correct size rows*colums*sizeof(double) . sizeof(rhs) is the size of the class object, which is unrelated to the array size. Since you are inside the copy assignment operator and not inside the copy constructor, the memory for mesh is already allocated, so you should remove mesh = new double [rows*columns]; from the copy assignment operator. You also have to do a return *this; there to match the return type.
  3. Because then you need the const version of the functor: double operator()(size_t i, size_t j) const
  4. read about rule of 3 or rule of 5, you should implement the copy constructor if you have a constructor and destructor.
  5. from @PaulMcKenzie: Use std::vector to not have to do 1, 2, or 4.

Here is a full example: https://ideone.com/seOSwN

I will try and add the missing pieces to your code:

  1. The copy constructor

  2. A working assignment operator.

After the code below, I will comment:

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

First, note the usage of the member-initialization list to initialize the members of the Mesh2D class. This is a good-habit to do things this way, instead of assigning inside the body of the constructor.


Second, note the assignment operator. It uses the copy / swap idiom to safely and cleanly deallocate the old memory and make a copy.


Third, the operator() should have a const overload, otherwise you could never use your class in something like this:

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

The reason being that m is a const reference, thus you can only call const functions on it. Thus, the operator() needs a const version, along with the non-const version.

Also, the reason why the non-const version of operator() needs to also exist is that you may want to do this:

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

If m had only a const version of operator() , the above operation could not be done due to operator() changing the internal state of the object.


Another issue is that you are using memcpy incorrectly. The memcpy works on the number of bytes, not the number of items in an array. Thus you need to multiply by sizeof(double) , since that's the byte count that you are copying. A more type-safe solution would be to use std::copy , which automatically uses the correct copying function using the number of items.


Last, the test for a bad "Mesh2D" has been removed from the assignment operator. The reason is that the assignment operator (and copy constructor) job is one thing and one thing only -- make copies.

When you start introducing business logic into the copy-assignment functions, you are risking making copies that are not copies, but instead pseudo-copies of the object being passed-in.

This leads to some of the nastiest and hardest-to-find bugs in C++, and that is when your copy-assignment functions want to play games and not really make copies. You know when you're doing something wrong if your copy constructor or assignment operator is making too many decisions and executing logic based on the passed-in value.


For completion, here is the same version of the class, but using std::vector<double> . Note how much smaller the code is, due to not needing a user-defined assignment operator, copy constructor, or destructor:

#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]; 
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM