Creating Array of Classes - c++

I got a class such as:
class me362
{
public:
void geometry(long double xLength);
void mesh(int xNode);
void properties(long double H, long double D, long double K,long double Q, long double DT,long double PHO,long double CP, long double TINF);
void drichlet(long double TLeft,long double TRight);
void neumann(bool Tlinks, bool Trechts);
void updateDiscretization(long double**** A,long double* b, long double* Tp);
void printVectorToFile(long double *x);
private:
int xdim;
long double xlength;
long double tleft;
long double tright;
long double h;
long double d;
long double k;
long double q;
long double dt;
long double cp;
long double rho;
long double Tinf;
bool tlinks;
bool trechts;
};
And I initialize it using
me362 domain1;
me362 domain2;
me362 domain3;
But I want to determine the number of domains that I want to initialize. So I need a dynamic array of me362 structures. How can I do that? Can it be done?
Thank you all,
Emre.

Yes, it can be done. Use std::vector instead which increases it's size dynamically on every push_back operation.
std::vector<me362> obj ;
for( int i = 0; i < numberOfInstancesRequired; ++i )
{
obj.push_back( me362() ) ;
}

Use std::vector, which handles dynamic memory for you:
#include <vector>
// ...
std::vector<me362> domains;
std::vector also has a lot of nice features and guarantees, like being layout-compatible with C, having locality of reference, zero overhead per element, and so on.
Also note that std::vector has a constructor that takes an integral argument, and creates that many elements:
// Will create a vector with 42 default-constructed me362 elements in it
std::vector<me362> domains(42);
See any standard library reference (like cppreference.com or cplusplus.com) for details about using std::vector.)

For starters, welcome to the world of STL(standard template library)!
In your case, you can use std::vector, as it can hold variable number of elements for you.
#include<vector>
using namespace std;
//Create a std::vector object with zero size
vector<me362> myVector;
//Insert new items
myVector.push_back(domain1);
myVector.push_back(domain2);
myVector.push_back(domain3);
//Get the size of the vector, i.e., number of elements in vector
myVector.size();
Besides, you can create a vector object like this.
//nMe362: number of elements in vector, me362Obj: init value of each me362 object
vector<me362> myVector(nMe362, me362Obj);

Related

Use an array with non-static size inside of a class

I want to use a class that can store a value for me inside of a 4d array (Matrix). I want to useSetSize in order to set the size of the Matrix from my main function.
class Value{
public:
int a;
int b;
int c;
int d;
//sets the values
void SetSize(long Sizea, int Sizeb, int Sizec, int Sized){
a = Sizea;
b = Sizeb;
c = Sizec;
d = Sized;
};
double Matrix[a][b][c][d];
void Putavalueinside (double input, long Positiona, long Positionb, long Positionc, long Positiond) {
Matrix[Positiona][Positionb][Positionc][Positiond] = input;
}
};
int main()
{
//makes a class object
Value Test;
//sets the size
Test.SetSize(3, 4, 5, 6);
//gives the Matrix a value
Test.Putavalueinside(10, 0, 0, 0, 0);
//prints it to the consol
std::cout << Test.Matrix[0][0][0][0];
}
However, the program does not compile and gives me the error:
|15|error: invalid use of non-static data member 'Value::a'|
Does anyone have an idea on how to solve this?
There are basically 2 ways to solve this. If you want to keep the array, then you need to move the dimension sizes from being constructor parameters to being template parameters. Template parameters are known at compile time and can be used to create an array. That would look like
template <size_t a, size_t b, size_t c, size_t d>
class Value{
public:
double Matrix[a][b][c][d]{};
void Putavalueinside (double input, long Positiona, long Positionb, long Positionc, long Positiond) {
Matrix[Positiona][Positionb][Positionc][Positiond] = input;
}
};
int main()
{
//makes a class object
Value<3,4,5,6> Test;
//gives the Matrix a value
Test.Putavalueinside(10, 0, 0, 0, 0);
//prints it to the consol
std::cout << Test.Matrix[0][0][0][0];
}
If you don't know what the sizes will be at compile time then you are going to need to do some dynamic memory allocation. To handle this you can use a std::vector as the storage type of the matrix and then you can use math to pretend it is a 4d structure. That would look like
class Value{
public:
size_t a;
size_t b;
size_t c;
size_t d;
std::vector<double> Matrix;
Value(size_t a_, size_t b_, size_t c_, size_t d_) : a(a_), b(b_), c(c_), d(d_), Matrix(a_ * b_ * c_ * d_) {}
void Putavalueinside (double input, long Positiona, long Positionb, long Positionc, long Positiond) {
Matrix[Positiona + Positionb * a + Positionc * a * b + Positiond * a * b * c] = input;
}
double Getvalueinside (long Positiona, long Positionb, long Positionc, long Positiond) {
return Matrix[Positiona + Positionb * a + Positionc * a * b + Positiond * a * b * c];
}
};
int main()
{
//makes a class object
Value Test(3,4,5,6);
//gives the Matrix a value
Test.Putavalueinside(10, 0, 0, 0, 0);
//prints it to the consol
std::cout << Test.Getvalueinside(0, 0, 0, 0);
}
The formula for flattening an n-dimension array can be found here: 4D position from 1D index?
Your array sizes need to be known at compile time if you want to avoid dynamic allocation. Possible implementation:
template <size_t a, size_t b, size_t c, size_t d>
class Value
{
double Matrix[a][b][c][d];
};
Also, consider using std::array. If you know the sizes only at runtime, you need to allocate dynamically (with new). Even better, use std::vector (which uses heap memory).
The size of a member array (as well as all automatic or static arrays) must be a compile time constant. So, what you're trying is not possible.
In order to have an array with non-static size, you must allocate the array dynamically. I recommend using std::vector to do the allocation.
Unfortunately, dynamic allocation means that you can only use a one dimensional array (only the outermost dimension of dynamic array may be determined at runtime). This is not a big deal however; the memory of a multi dimensional array is just as flat as the single dimensional array; this just means that you have to calculate the index instead of the compiler doing it for you.
Here is an example layout of a 2 x 2 x 2 x 2 array laid flat:
0123456789abcdef <- flat index
0000000011111111 <- dimension a
0000111100001111 <- dimension b
0011001100110011 <- dimension c
0101010101010101 <- dimension d
In C++ you should use hardly, if ever, C-style dynamic arrays (i.e. using new). You should define your Matrix as
std::vector<std::vector<std::vector<std::vector<double>>>> Matrix;
(I know it is ugly, but thats how it is, you should probably add a using matrix_t = std::vector<std::vector<std::vector<std::vector<double>>>>; somwhere, so you only have to type that once). Then initialize this vector in the constructor.
As constructor use
Value(size_t a, size_t b, size_t c, size_t d) :
Matrix({a, {b, {c,std::vector<double>(d,0.0)}}})
{}
You can also us a one-dimensional vector of size a*b*c*d and calculate the index in this array from the 4 indices in the matrix.
As you've written things, the field Value::Matrix must be allocated when you create a Value object, because its size determines the size of the Value object. This size can't be changed later on. (In fact, the size of an object needs to be a compile-time constant, so that it can be stack-allocated, made into arrays, etc.)
If you want something whose size is going to be determined at runtime, it needs to be heap-allocated, e.g. by using new[]. (There are various ways of wrapping new[] with higher-level constructs so that you don't have to keep track and delete[] the result when you're done with it, e.g. std::unique_ptr or std::vector, but you should learn how to work with pointers first so you understand what these are even doing and why.)
Also, you need to take into account the fact that a multidimensional array in C++ is an array of pointers to arrays. This is bad for performance due to extra indirection and poor cache locality, so you may want to implement things as a one-dimensional array under the hood.
Alternatively, if the size is known at compile time, it could be given as a template parameter:
template<int A, int B, int C, int D>
class Value
{
int a = A;
int b = B;
int c = C;
int d = D;
double Matrix[A][B][C][D];
//...
};
// ...
Value<3, 3, 2, 5> val;
Note that this still has the issues with multidimensional arrays mentioned above.

Code stores a vector as a vector<vector>, why no error message?

I'm working on a piece of C++ code left by a predecessor, and it apparently stores a vector<long int> as a vector<vector<long int>>. The code compiles and runs, but I don't understand why. Here's the function that does the storing.
void setPotentialParameters(const int& seed, const int& nMax, const double& lambdaStd, const int fieldNum, potentialPars& par)
{
gsl_rng * r = gsl_rng_alloc (gsl_rng_taus);
gsl_rng_set (r, seed);
par.nMaximum= nMax;
par.fNum= fieldNum;
for (int i=0; i<100; i++) gsl_ran_gaussian (r, lambdaStd);
int counter=0;
vector<long int> tempNs(fieldNum); //Defines tempNs as a vector<long int>
for (long int i=0; i< (-0.2+pow(2*nMax+1, fieldNum)); i++) {
findPartition(i, fieldNum, 2*nMax+1, tempNs );
for (int i = 0; i < tempNs.size(); i++) {
tempNs[i] -= nMax;
}
if (goodPartition(tempNs, nMax)) {
counter++;
par.lambdas.push_back(abs( gsl_ran_gaussian (r, lambdaStd)));
par.nVals.push_back(tempNs); //Stores tempNs in nVals
par.alphas.push_back(2*M_PI * gsl_rng_uniform (r));
};
};
};
And this is the struct that tempNs is stored in.
struct potentialPars{
int nMaximum;
int fNum;
vector<double> lambdas;
vector<vector<long int> > nVals; //Defines nVals as a vector<vector<long int>>
vector<double> alphas;
};
I marked the three most relevant lines with comments. tempNs only has one element (as seen from the tempNs[i] -= nMax line), consistent with its definition as a vector<long int> but when nVals is called elsewhere in the program it has two elements, also consistent with its definition as a vector<vector<long int>>. It doesn't seem possible. Even though tempNs is modified by the findPartition function, it should still remain a vector of long integers. What am I missing?
A vector<vector<long> > has elements of type vector<long>.
A vectors push_back() method copies an element to the vector.
In code you've shown, par.nValues is of type vector<vector<long> > so pushing tempNS - which is of type vector<long> - is completely appropriate.
There is no problem in using a vector of vectors of longs.
Its almost the same as using a two-dimensional array, but you don't need to know the size at compile time or manage memory allocation.
There is no problem for compiling that code. Vector elements can be primitives(int, float, double), pointers or other objects(like vector or your user-defined classes).
The constraints on the (first) type parameter of std::vector are fairly lax. Almost any non-reference type can be stored in a std::vector, including std::vectors of something else. This is exactly what this code is doing.
You could wrap std::vector<long int> in a
struct partition {
std::vector<long int> indexes;
double lambda;
double alpha;
};
and change potentialPars to
struct potentialPartitions {
int nMaximum;
int fNum;
std::vector<partition> partitions;
};
which would add clarity, but would change how the consumer of potentialPartitions accesses those values.

C++ numerical integrators to solve systems of ode's

I recently started using C++ and I just created a class that allows the integration of a user-defined system of ode's. It uses two different integrators in order to compare its performance. Here is the general layout of the code:
class integrators {
private:
double ti; // initial time
double *xi; // initial solution
double tf; // end time
double dt; // time step
int n; // number of ode's
public:
// Function prototypes
double f(double, double *, double *); // function to integrate
double rk4(int, double, double, double, double *, double *);
double dp8(int, double, double, double, double *, double *);
};
// 4th Order Runge-Kutta function
double integrators::rk4(int n, double ti, double tf, double dt, double *xi, double *xf) {
// Function statements
}
// 8th Order Dormand-Prince function
double integrators::dp8(int n, double ti, double tf, double dt, double *xi, double *xf) {
// Function statements
}
// System of first order differential equations
double integrators::f(double t, double *x, double *dx) {
// Function statements
}
int main() {
// Initial conditions and time related parameters
const int n = 4;
double t0, tmax, dt;
double x0[n], xf[n];
x0[0] = 0.0;
x0[1] = 0.0;
x0[2] = 1.0;
x0[3] = 2.0;
// Calling class integrators
integrators example01;
integrators example02;
// First integrator
example02.dp8(n, t0, tmax, dt, x0, xf);
// Second integrator
example01.rk4(n, t0, tmax, dt, x0, xf);
}
The problem is that the array containing the initial conditions x0 in main, changes after executing the first integrator and I cannot use the same initial conditions for the second integrator, unless I define another array with the same initial conditions (x0_rk4 and x0_dp8). Is there a more professional way to keep this array constant in order to use it in both integrators?
The easiest way is to make a local copy of array inside integrating functions.
Change the way you are passing 'n' to function to 'const int n', so you can make something like double currentSolution[n]; inside and copy elements from initial array to new one. This approach will save your initial array intact, unless you will "accidently" modify it somewhere.
To prevent this probability of accident modification we need to go deeper and use one of stl containers. I think you will be fine with std::valarray<T>.
Change the way you are passing it to const std::valarray<double>& and again make non-const local copy.
No, not really. But there exist a more elegant solution:
std::array<double, n> x0_rk4 = { 0.0, 0.0, 1.0, 2.0 };
auto x0_dp8 = x0_rk4; // copy!
You will have to use x0_rk4.data() to access the underlying array. Note that it would be better if you used std::array and other modern C++ features instead of raw pointers and the like.

Using a 2-d Array in a function

I am trying to solve a dynamic programming problem and I need to take the user input in the form of a 2-d array and use the values from the 2-d array inside the function.
The values of the 2-d array will not be changed when used inside the function.
In the function int dp i am getting the
error:
declaration of 'a' as multidimensional array must have bounds for all dimensions except the first
int max(int a,int b,int c)
{
if(a>=b && a>=c)return a;
if(b>=c && b>=a)return b;
else return c;
}
int max2(int a,int b)
{
if(a>b)return a;
else return b;
}
int dp(int i,int j,int a[][],int p,int q)
{
if((i-1)>=0 && (j-1)>=0 &&(i+1)<p &&(j+1)<q )
return max(a[i][j]+dp(i-1,j+1,a,p,q),a[i][j]+dp(i+1,j+1,p,q),
a[i][j]+dp(i,j+1,p,q));
if(i==0 && j!=0 && (j+1)<q)
return max2(a[i][j]+dp(i+1,j+1,p,q),a[i][j]+dp(i,j+1,p,q));
}
int main()
{
int p,q,r,s,T,a,b,i,j,k;
scanf("%d",&T);
for(a=0;a<T;a++)
{
scanf("%d %d",p,q);
int z[p][q];
int max=0;
for(i=0;i<q;i++)
{
for(j=0;j<p-1;j++)
scanf("%d ",&z[j][i]);
scanf("%d",&z[j+1][i]);
}
for(i=0;i<p;i++)
{
if(dp(i,0,z,p,q)>max)
max=dp(i,0,z,p,q);
}
}
}
It's all in the error message:
declaration of 'a' as multidimensional array must have bounds for all dimensions except the first
Your function signature does not have bounds for a's 2nd dimension:
int dp(int i,int j,int a[][],int p,int q)
// ^^^^^
You need to fill it in with a[][N] where N is whatever the correct bound is. The issue is that you are using VLAs here:
scanf("%d %d",p,q);
int z[p][q];
That is non-standard C++, and basically means you cannot write the signature of dp, since the second bound has to be known as a compile-time constant. You could either make it a single-dimensional array:
int* z = new int[p*q];
int dp(int i, int j, int* a, int p, int q)
// ^^^^^^
or dynamically allocate it in 2 dimensions and just pass it in that way:
int** z = new int*[p];
for (int i = 0; i < p; ++i) {
z[i] = new int[q];
}
int dp(int i, int j, int** a, int p, int q)
// ^^^^^^^
The function dp needs some information to perform meaningful index calculations, either done by the compiler or in the actual inplementation. Either a dimension must be specified in the type or the argument a could be of type int** while its dimensions are provided as separate arguments to dp. As this is C++, a type of std::vector< std::vector< int > > might be more suitable for the task.
You get that error because you cannot leave both the index(row,column) empty in int a[][] in your function declaration. You must have both specified or atleast the value of column index.
Use dynamic declaration
int **z = new int*[p];
for (int i = 0; i < p; i++)
z[i] = new int[q];
Change the parameter int a[][] to int **a
You can't dynamically declare an array on the stack as the size has to be known at compile time. The only way to do this would be by allocating memory for the array on the heap using the new keyword, then you could declare the size at run time.
Far easier, however, would be just to use a container class, or in your case, a container of containers like a vector of vector of ints;
#include <vector>
vector< vector<int> > arrArray(rows, vector<int>(columns));
The syntax might look a bit strange, but breaking it down;
vector<int> - a vector of type int
vector< vector<int> > - a vector of vectors of type int
arrArray(rows, vector<int>(columns)); - here in the first parameter, we are saying; create rows number of vector<int>'s in our array, and the second parameter initalises the array to some value. If it were just a 2D array of int, we might initalise it to 0, or omit the second parameter and rely on the default value of int. But, because our multidimensional vector also contains vectors, we set each row of our main vector to store a vector of int's which holds columns amount of integers.
Now you can access the array like you would any other;
arrArray[2][0] = 5;
You also get all the added benefits that container classes contain, including iterators and a lot of useful class methods for manipulating and checking your array. Once you understand the syntax of creating container classes, you'll find them much easier to work with than arrays. You also don't have to worry about having to manage your own memory, and have the ability to do bounds checking before accessing vector elements.

creating a vector of pointers that point to more vectors

I am trying to create a vector that contains pointers, each pointer points to another vector of a type Cell which I have made using a struct.
The for loop below allows me to let the user define how many elements there are in the vector of pointers. Here's my code:
vector< vector<Cell>* > vEstore(selection);
for (int t=0; t<selection; t++)
{
vEstore[t] = new vector<Cell>;
vEstore[t]->reserve(1000);
}
This, I think, gives me a vector of pointers to destination vectors of the type Cell.
This compiles but I'm now trying to push_back onto the destination vectors and can't see how to do it.
Since the destination vector is of the type Cell which is made from a type as follows:
struct Cell
{
unsigned long long lr1;
unsigned int cw2;
};
I can't work out how to push_back onto this destination vector with 2 values?
I was thinking ...
binpocket[1]->lr1.push_back(10);
binpocket[1]->cw2.push_back(12);
As I thought this would dereference the pointer at binpocket[1] revealing the destination array values, then address each element in turn.
But it doesn't compile.
can anyone help
...but this only has one value and doesn't compile anyway.
Cell cell = { 10, 12 };
binpocket[1]->push_back(cell);
Alternatively, you can give your struct a constructor.
struct Cell
{
Cell() {}
Cell(unsigned long long lr1, unsigned int cw2)
: lr1(lr1), cw2(cw2)
{
}
unsigned long long lr1;
unsigned int cw2;
};
Then you could do
binpocket[1]->push_back(Cell(10, 12));
Note that long long is non-standard (yet), but is a generally accepted extension.
Give your cell a constructor:
struct Cell
{
unsigned long long lr1;
unsigned int cw2;
Cell( long long lv, int iv ) : lr1(lv), cw2(iv ) {}
};
You can now say things like:
binpocket[1]->push_back( Cell( 10, 12 ) );
BTW, note that long long is not standard C++.