Hello everyone:

I am a C++ newbie. I aim to use C++ in my work on coupled fluid flow-chemical reaction problems. I would very much appreciate some help to get me on the right track! I apologize in advance for what is probably a very simple question.

My goal is to come up with C++ code for solving a finite difference problem. To do this, I need to (1) construct a 2-D grid and (2) link a struct of data (fluid properties) to the grid. At some point, as I become more experienced, I'd like to learn how to use pointers and iterators, etc, but for now I would like to keep things as simple as possible.

Using some examples, I have come up with a way of constructing a grid that mostly makes sense. But I cannot figure out how to link the grid with a struct of fluid properties. Sorry--I'm certain this is a simple thing to do. Once I do have this figured out, I have questions about the grid construction. The main question about the grid for now is I don't understand why it is a good idea to use a template. I only used one because all the examples I've seen use one.

Here is my grid. Please keep in mind that I am a complete newbie! Thanks:

template <typename T>	
class Grid2D
{
public:
Grid() : xsize_(0), ysize_(0), grid_(0) { }	
Grid(double xsize, double ysize) : xsize_(xsize), ysize_(ysize), grid_(new T[ xsize_ * ysize_ ]) { }

~Grid() { delete[ ] grid_; }	
Create(double xsize, double ysize) 
{
xsize_ = xsize; 
ysize_ = ysize;
grid_ = new T[ xsize_ * ysize_ ];	
}
Resize(double xsize, double ysize) 
{
xsize_ = xsize; 
ysize_ = ysize; 
delete[ ] grid_;	
grid_ = new T[ xsize_ * ysize_ ];
}
Delete()	
{
xsize_ = 0;
ysize_ = 0;
delete[ ] grid_;
grid_ = 0;
}
double xsize() const { return xsize_; }	
double ysize() const { return ysize_; }	
T& operator(double x, double y) { return grid_[ y*xsize_ + x ]; }	
const T& operator(double x, double y) const { return grid_[ y*xsize_ + x ]; }	
private: 
double xsize_, ysize_;
T* grid_;
}

Hi,

I take it it does not compile. I think you would need a good c++ book. Because just to pick things up to create what you are trying to create.

Firstly before the template declaration you need the statement "using namespace std;"

the class members xsize, ysize, grid need types

Then your constructors need to be named exactly as the class for which they are defined for e.g

using namespace std;
template <typename T>	
class Grid2D
{
int xsize, ysize, grid;
public:
Grid2D() : xsize(0), ysize(0), grid(0) { }

Hi GrubSchumi:

Thanks for pointing out these issues. I am aware that this won't compile right now. I pulled this together from a working version of the code. Thanks for your input.

Hi,

Firstly, you would need to construct a data struct defined by you. I do not know the details of the struct properties but the struct would look something like this:

struct fluidProperties //this struct contains the properties
{
        string name; //name of the fluid this type is only for example. 
       //some type property1;
       //some type property2;
       //some type property3;
      //ect...
};

The class then appears although at this stage we can go straight to the main method because you want to link the the struct of fluid properties to the grid but what we are actually creating is a grid of struct fluidProperties. What we can do is make a 2d array (grid) of struct fluidProperties e.g:

template <typename T>	
class Grid2D
{
private: // or public: which ever suits your needs
  T grid[10][10];   
};

int main()
{
Grid2D<fluidProperties> grid2DObject;
return 0;
}

The reason we have Grid2D<fluidProperties> grid2DObject; is because of the template keyword used prior to declaring the class Grid2D. This means that the typename T are fluidProperties data types. You could also say Grid2D<int> grid2DObject that now means typename T are of int data type.

T grid[10][10] is a 10 * 10 grid you declare the object like this if you know in advance the size of the grid however if the size of the grid is subject to change depending on your needs you might want to dynamically allocate the memory i.e the size of grid2D grid is not always fixed by doing this:

template <typename T>	
class Grid2D
{
  T **grid;    
  Grid2D(int x, int y)
  {
            grid = new T*[y]; 
            for(int i=0;i<=y; i++)
            {
                    grid[i] = new T[x];
            }          
  }     
};

int main ()
Grid2D<fluidProperties> *grid2DObject = new Grid2D(10,10)// or (20, 20) whatever size fits your needs;

You do not need the template I think for what you are trying to do so my suggestion would read something like this:

using namespace std;
struct dataFluids
{
       //some type xsize;
       //some other type ysize;       
};
	
class Grid2D
{
      
  dataFluids **grid;
  public:    
  Grid2D(int x, int y)
  {
            grid = new dataFluids*[y]; 
            for(int i=0;i<=y; i++)
            {
                    grid[i] = new dataFluids[x];
            }          
  }     
 // other class methods for what you want to do proceed.... 
};
int main()
{
Grid2D *a = new Grid2D(10,10)//or (10, 20) or (20, 10) or whatever fits your needs;   
 return 0;
}

I think this is what you want although it is very skeletal, I hope it does help you. If there are anymore questions or for someone to elaborate please feel free to ask again.

Edited 7 Years Ago by GrubSchumi: n/a

Hi Grubschumi:

Thank you very much for all your time and effort.

Although you suggested that it is not necessary to use a template, I think I'll stay with it for now... Your alternative method looks elegant, but unfortunately I am not yet advanced enough to understand the double pointers involved. I will certainly go through it and try to figure out what is going on though! Other than the elegance/relative simplicity of the double-pointers method you described, can you tell me whether there are any other advantages of it over the template method I'm using (I'd like to learn good habits!)?

I have two further questions for now, if you have time!

1) So, regarding the template method you described first, what you are saying is that the whole point of the template is so that I can take a struct or class of fluidProperties and have them have all the functionality of the template class, Grid2D?

2) Where I'm getting confused is in defining the size the the grid. In your first piece of code below the struct definition, you have this:

T grid[10][10];

Here is another dumb question... I have seen examples where people have instead used "resize", for example:

grid2DObject.Resize(5,6);

My question is, would "grid2DObject.Resize(5,6);" work here? My understanding is that .resize is associated with the STL containers like vector. If I wanted to use resize, would I have to define it as a member funtion of the Grid2D class?

Thanks again for your help!! Much appreciated.

Edited 7 Years Ago by Annettest: Realized the original was very unclear

Hi,

Ok your first question:

1) So, regarding the template method you described first, what you are saying is that the whole point of the template is so that I can take a struct or class of fluidProperties and have them have all the functionality of the template class, Grid2D?

A) Yes. The template <typename T> allows for flexibility in that typename is the data type like int, or a class and struct defined by the user. <typename T> can be any type and is used if to make a generic class where the <typename T> needs to change depending on the needs of the user. So we have the generic class declared and I want to use the datatype int I would correctly write:

Grid2D <int> grid;//correct

That means every instance of T in the class is now an int. At the same time I want to use the same class but with fluidProperties, I will say:

Grid2D <fuildProperties> grid; //correct as well

All references to 'T' in Grid2D now are of type fluidProperties
without any need to make modification to the original declaration of class Grid2D.

2) Where I'm getting confused is in defining the size the the grid. In your first piece of code below the struct definition, you have this:

T grid[10][10];

But I already define my grid size parameters in the Grid2D class. How does your line above fit in with what I already defined in the Grid2D class? In other words, you have this line before the call to main. I don't understand this.


Yours reads:

Grid(double xsize, double ysize) : xsize_(xsize), ysize_(ysize), grid_(new T[ xsize_ * ysize_ ]) { }

Yours is also correct what it does is create a single dimension array. for example if we say xsize_ = 5 and ysize_ = 5 we are simply making a single dimension array of [25] because new T(xsize_ 8 ysize_] is the same [25] it is then upto your imagination as to how you will code rules to govern it so it works as a 2D array i.e a grid. Technically a a 2D grid of n * n e.g. 5 * 5 which has 25 slots five down and five across.

To say T grid [10][10] or T grid [5][5] defines the 2D array already, so there is no need to code in the rules to govern it to the 2D behavior. This is not dynamic. It means you would need to change the code according to the size each time.

using namespace std;
struct dataFluids
{
       //some type xsize;
       //some other type ysize;       
};
	
class Grid2D
{
      
  dataFluids **grid;
  public:    
  Grid2D(int x, int y)
  {
            grid = new dataFluids*[y]; 
            for(int i=0;i<=y; i++)
            {
                    grid[i] = new dataFluids[x];
            }          
  }     
 // other class methods for what you want to do proceed.... 
};
int main()
{
Grid2D *a = new Grid2D(10,10)//or (10, 20) or (20, 10) or whatever fits your needs;   
 return 0;
}

allows for Dynamic size allocation.
Hope this helps.

Edited 7 Years Ago by GrubSchumi: n/a

Hi GrubSchumi:

Thank you so much. This has switched a lightbulb on for me!! You really have a knack for detailed, clear explanation. I hope you won't mind if I check back in a couple of days.

Hi GrubSchumi:

If you have time for two more questions (please don't feel obliged):

1) I'm not sure that I am clear on what is going on here (from yesterday's code example). What is your translation? I am particularly confused about where x and y are coming from.

T& operator(double x, double y) { return grid_[ y*xsize_ + x ]; }

2) Also, where would you suggest putting my rules for resizing the array from 1D to 2D?

Thanks again.

Hi Annettest,

Sorry for the delay. Ok on to the first question or I may be able to answer both at once:

1) I'm not sure that I am clear on what is going on here (from yesterday's code example). What is your translation? I am particularly confused about where x and y are coming from.

T& operator(double x, double y) { return grid_[ y*xsize_ + x ]; }

A) double x, double y are parameters being passed to the operator that is being overloaded. in C++ we have function overloading where and inheriting class changes the functionality of a function it inherited from a parent class. We also have operator overloading where we change the functionality of the operator. examples of operators in c++ are the following:

+
==
=
-
*
new
delete
etc..........

There are quite a few to list all. In the code you have the operator appears to be missing because it should read:

T& operator+(double x, double y) { return grid_[ y*xsize_ + x ]; }
//or
T& operator==(double x, double y) { return grid_[ y*xsize_ + x ]; }
//or
T& operator new(double x, double y) { return grid_[ y*xsize_ + x ]; }

depending on the operator you are overloading. Right now as it is, it will not compile.

The double x and y parameters are used to find the location in the array. For example imagine we have an array please keep in mind we have a single dimension array but we are treating it as a 2D array (grid).

let us look at the single dimension array as it is single. Lets say a 3 * 3 grid for simplicity's sake. The variables in the class you have will have the following values. xsize_ = 3, ysize_ = 3 giving us a 3 * 3 grid, yes?

The 3 * 3 single dimension array would look something like this with the following slots: [0][1][2][3][4][5][6][7][8] the array is available up to 8 but all together has 9 slots as 0 is an available slot.

But In our minds the array looks like this:
[0][1][2]
[3][4][5]
[6][7][8]

and as such the code we write must be in such a fashion that information we store and extract is done so as if it where a grid.
Columns
Rows[ ][ ][ ]
Rows[ ][ ][ ]
Rows[ ][ ][ ]


So the following :

T& operator(double x, double y) { return grid_[ y*xsize_ + x ]; }

double x and double y Are the rules we are writing to maintain the illusion of a grid:
[0][1][2]
[3][4][5]
[6][7][8]

But in reality we have: [0][1][2][3][4][5][6][7][8]

x refers to the column number, and y to the row number. So we multiply the row number (y) by xsize_ (3) (remember from above xsize_ = 3, ysize_ = 3 for the 3 * 3 grid).

T& operator(double x, double y) { return grid_[ y*xsize_ + x ]; }

return grid_[ y*xsize_ + x ]; is the manner in which we code the 2D behavior. Let us look at the two examples above with a 3 * 3 grid.

If I called the operator with figures

T& operator(2,2) { return grid_[ 2*3 + 2 ]; }
//is pretty much this:
T& operator(2,2) { return grid_[8]; }

with column x = 2 and row y = 2 we would return the value 8 in our phantom grid.
[0][1][2]
[3][4][5]
[6][7][8]

and this is consistent in the real single dimension array: [0][1][2][3][4][5][6][7][8]

We must remember that column 0 and row 0 exist so while 8 looks like it is in column 3 row 3 in c++ it is column 2 row 2 because column 0 and row 0 exist and it has the value 0.

I hope this answers the question. Please again if if I not clear enough please come back and do not be afraid to ask, I am more than happy to answer if I can.

Edited 7 Years Ago by GrubSchumi: n/a

Hi GrubSchumi:

Once again, thanks for your help and time. I really appreciate it.

Hi GrubSchumi:

If you have time, I have a question about splitting up code into header, .cpp, and main files. I very much appreciate the help you have already provided, so please don't feel obliged to deal with yet another basic question!

Background: my goal is to learn C++ so that I can solve finite difference/finite element problems. I have been working on using the template class method you helped me with previously to make a 2D array. At each array/grid point (x, y), I would like to specify properties like viscosity and density. These properties are listed within a struct.

Current problem: I have managed to come up with a single file that compiles. However, I am unable to split the file up into a header, a .cpp, a struct (of properties), and a main file. I keep getting errors that seem to be related to scope. I have tried a bunch of different things to fix the errors... I won't list them all here because it would be too much! Also, I may be on the wrong track. The reason I want to split the single file up is that I'd like to learn how to do this; also, things are going to get complicated as I build on this code.

Question: if you have time and this is something you are familiar with, I would very much appreciate it if you could suggest how to go about splitting a single file into a header and .cpp (array), a struct (properties), and a main file. Please don't feel like you have to perfect the example below--I only included it to give you an idea of where I'm coming from. Perhaps you could describe a shorter example, that shows the correct syntax in each file needed to correctly deal with scopes. I would particularly like to know how to instantiate the Grid2D class in the main file to make a grid of properties in a struct (in separate file).

template <typename T>	
class Grid2D
{
public:
Grid2D()
: xsize_(0), ysize_(0), grid_(0) 
{ }	
Grid2D(double row, double col) 
: xsize_(row), ysize_(col), grid_(new T[ xsize_ * ysize_ ]) 
{ }	

~Grid2D() { delete[ ] grid_; }	

Make(double row, double col) 
{
xsize_ = row; 
ysize_ = col;
grid_ = new T[ xsize_ * ysize_ ];	/
}
Resize(double row, double col) 
{
xsize_ = row; 
ysize_ = col; 
delete[ ] grid_;	
grid_ = new T[ xsize_ * ysize_ ];	
}
Delete()	
{
xsize_ = 0;
ysize_ = 0;
delete[ ] grid_;
grid_ = 0;
}
double Row() const { return xsize_; }	
double Col() const { return ysize_; }	
T& operator(double x, double y) { return grid_[ y*xsize_ + x ]; }	 // Overload x and y
const T& operator(double x, double y) const { return grid_[ y*xsize_ + x ]; }	
private: 
double xsize_, ysize_;	
T* grid_;	
}
	

//------------------------------------

struct Props
{
double foo;
double bar;	 // … etc
};

//---------------------------------

int main()
{
// make a grid of Props
Grid2D<Props> grid;

// etc
This article has been dead for over six months. Start a new discussion instead.