I implemented a simple class for n-body simulations in C++. However, the class uses a lot of old C style arrays which I want to replace with data structures that the STL offers.
Here is the relevant part of my code that I want to improve:
struct Particle{
double m; // mass
double x[DIM]; // position
double v[DIM]; // velocity
double F[DIM]; // force
};
class Nbody {
private:
const unsigned int n; // number of particles
const double dt; // step size
const double t_max; // max simulation time
std::vector<Particle> p;
public:
~Nbody(){};
Nbody(unsigned int n_, double dt_, double t_max_);
};
Nbody::Nbody(unsigned int n_, double dt_, double t_max_)
: n{n_}, dt{dt_}, t_max{t_max_} {
p = new std::vector<Particle> [n];
}
I tried to use std::vector<Particle>. But how do I in this case initialize n particles correctly? My current approach does not work and the compiler throws many errors. How do I do it correctly?
p is not a pointer. p is declared as a vector.
Rewrite the constructor definition like
Nbody::Nbody(unsigned int n_, double dt_, double t_max_)
: n{n_}, dt{dt_}, t_max{t_max_}, p( n_ ) {
}
In this case the vector p is initialized as a vector having n elements that are value initialized.
Also in the definition of the structure Particle you could substitute the arrays to objects of the type std::array<double, DIM>. Also it is better to make the constant DIM either as enumerator of the structure or as a static data member of the structure/
new std::vector<Particle> [n] dynamically allocates an array of n empty vectors and produces a pointer to the first one.
That is not the same as a vector with n elements.
You should use the initialiser list:
Nbody::Nbody(unsigned int n_, double dt_, double t_max_)
: n{n_}, dt{dt_}, t_max{t_max_}, p{n_}
{
// Empty.
}
Assuming that n tracks the number of particles, you can get rid of it and use p.size() instead.
Initialisation of the particles themselves should be added to Particle.
struct Particle{
double mass = 0.0;
double position[DIM] = {};
double velocity[DIM] = {};
double force[DIM] = {};
};
or
struct Particle{
double mass = 0.0;
std::array<double, DIM> position;
std::array<double, DIM> velocity;
std::array<double, DIM> force;
};
To change the size of a vector, you can use:
p.resize(n);
All new elements will be default-constructed, which in this case means they will contain garbage values.
Related
I have a class that has a 3D vector as one of it's variables. This size of this vector won't be known until runtime. Is there an efficient way to initialise this vector?
For example, my class may be
class Foo {
public:
std::vector<std::vector<std::vector<float>>> x;
std::vector<std::vector<std::vector<float>>> y;
std::vector<std::vector<std::vector<float>>> z;
std::vector<std::vector<std::vector<float>>> bar;
int ni;
int nj;
int nk;
}
with a constructor
Foo::Foo(std::vector<std::vector<std::vector<float>>> x_,
std::vector<std::vector<std::vector<float>>> y_,
std::vector<std::vector<std::vector<float>>> z_) {
x = x_;
y = y_;
z = z_;
ni = x.size();
nj = x[0].size();
nk = x[0][0].size();
std::vector<std::vector<std::vector<float>>> tmp(ni, std::vector<std::vector<float>>(nj, std::vector<float>(nk)));
bar = tmp;
}
Can I do the last two lines of the above without having to assign the dummy variable tmp?
This is how you could do it (but don't miss to read the end):
#include <vector>
class Foo {
public:
std::vector<std::vector<std::vector<float>>> x;
std::vector<std::vector<std::vector<float>>> y;
std::vector<std::vector<std::vector<float>>> z;
int ni;
int nj;
int nk;
using inner_type = std::vector<float>;
using middle_type = std::vector<inner_type>;
using outer_type = std::vector<middle_type>;
outer_type bar;
Foo(outer_type x_,
outer_type y_,
outer_type z_) :
x(x_),y(y_),z(z_),
ni(x.size()),
nj(ni ? x[0].size() : 0),
nk(nj ? x[0].size() : 0),
bar( outer_type(ni,middle_type(nj,inner_type(nk))))
{
}
};
Members are initialized before the constructor body is executed, thats why I used the member initializer list. And I changed the order of the members, because members are initialized in the order they appear in the class definition. The access to x[0] made me a bit nervous, so I tried to make sure empty vectors don't cause havoc.
This works and does what you want (I hope), but the vectors are populated with copies of the temporaries passed to their constructor, which isn't quite efficient. As an alternative you can resize the member as suggested in this answer.
Last not least, reconsider if you really want a std::vector<std::vector<std::vector<float>>>. If you need all "rows" to have same number of "columns" then a nested vector makes you pay for something you do not use. Moreover, the most attractive feature of std::vector is its memory-locality. Though, the floats in a std::vector<std::vector<float>> are stored in fragmented areas of memory (as the elements are not stored directly in the vector).
A flat std::vector<float> with appropriate index transformation is often the better choice.
float& access_element(size_t i, size_t j, size_t k) {
return bar[ i *offset_i + j*offset_j + k];
}
You could use resize() and a couple for loops to set bar. It's not the prettiest solution, but it should have pretty good performance as no temporaries are create and there are no unnecessary assignments. That would look like
bar.resize(ni);
for(auto& twodee : bar)
{
twodee.resize(nj);
for(auto& onedee : twodee)
onedee.resize(nk);
}
And now bar has the same size and is filled with zeros.
I am new to C++ which is the reason why I'm currently kind of stuck.
Now here's the Problem: I have a couple of float matrices like this:
static const float matr1[4][8] = {0.0, 0.0, ...};
static const float matr2[7][8] = {0.0, 0.5, ...};
etc.
I have a struct like to this one:
struct structy{
float matr[][];
int index;
float somevalue;
};
I have a vector of this structy which is created dynamically dependent on other information.
How can I reference a certain of these declared matrices in my struct variable, given that the first parameter of the struct (rows) varies?
I need a row of the matrices as a float array later on.
Thanks for your help!
You should also store the number of columns and the number of rows in structy, so that you know the dimensions of matr at a later point in time. There is no way to check the length of the 2D array otherwise.
As for your main question: Are you having troubles accessing individual matrix entries in one of your 2D float arrays (matr)?
The only way I have ever seen this done is dynamically:
struct structy{
float ** matr;
// Need to add these 2 variables
int dimensionRow;
int dimensionCol;
int index;
float somevalue;
};
When you place the data into matr, you need to also set dimensionRow, and dimensionCol, as well as dynamically allocate matr prior to filling it, IFF you plan to copy. If not you can simply set matr to the pointer of one of your pre-defined matrices. Either way you will need to also set dimensionRow and dimensionCol.
If you need varying sized matrices, I'd suggest using a vector of vectors. This will save you from the trouble of manually allocating a 2D array and managing its memory. One possible implementation:
struct structy {
std::vector< std::vector<float> > matr;
int index;
float somevalue;
};
structy s;
...
s.matr[0][1] = 42.0f;
...
And either grow the vectors on demand using push_back() or grow them beforehand with resize().
Now if you just want a reference to an external matrix (pointer to static memory) then you can just declare a pointer to pointer (double pointer):
struct structy {
const float ** matr;
int index;
float somevalue;
};
You cannot create a reference (not in the traditional sense) that will refer to different types, and arrays with different lengths are different types. However, you can take advantage of the fact that arrays are contiguous, (and so arrays of arrays are contiguous arrays), and create a class that acts as a reference using pointers.
template<typename T>
class matrix_reference
{
public:
template<size_t R, size_t C>
void set_reference(T (&arr)[R][C])
{
m_start = &arr[0][0];
m_rows = R;
m_columns = C;
}
T& operator()(size_t r, size_t c)
{
return m_start[r * m_columns + c];
}
size_t rows() const { return m_rows; }
size_t columns() const { return m_columns; }
private:
T* m_start;
size_t m_rows;
size_t m_columns;
};
int main()
{
matrix_reference<const float> mref;
mref.set_reference(matr1);
for (size_t r=0; r<mref.rows(); ++r)
{
for (size_t c=0; c<mref.columns; ++c)
std::cout << mref(r,c) << ' ';
std::cout << '\n';
}
mref.set_reference(matr2);
for (size_t r=0; r<mref.rows(); ++r)
{
for (size_t c=0; c<mref.columns; ++c)
std::cout << mref(r,c) << ' ';
std::cout << '\n';
}
}
I am trying to initialize pointer to struct array in my class constructor but it do not working at all...
class Particles {
private:
struct Particle {
double x, y, z, vx, vy, vz;
};
Particle * parts[];
public:
Particles (int count)
{
parts = new Particle [count]; // < here is problem
}
};
Remove those [] from declaration. It should be
Particle *parts;
Using C++, you can use benefits of std::vector:
class Particles {
// ...
std::vector<Particle> parts;
public:
Particles (int count) : parts(count)
{
}
};
Particle * parts[];
This is an array of pointers. To initialise this, you would need to loop through the array, initialising each of the pointers to point at a dynamically allocated Particle object.
You probably want to just make parts a pointer:
Particle* parts;
The new[] expression returns a pointer to the first element of the array - a Particle* - so the initialisation will work just fine.
Try this:
class Particles {
private:
struct Particle {
double x, y, z, vx, vy, vz;
};
Particle * parts;
public:
Particles (int count)
{
parts = new Particle [count]; // < here is problem
}
};
I am having a bit of trouble with C++ classes. I am writing a class (cSpline) to do a cubic spline fit on a set of input points. I want to use a cSpline object within another class (ss304, which provides properties of type 304 stainless steel as a function of temperature). I have four files. Here is the code I have come up with:
cSpline.h:
class cSpline {
private:
double *setCoeffs(double* x_in, double* f_in, int size);
double *coeffs;
double *f;
double *x;
int sizex;
public:
double *interpolate(double* y, int sizey);
cSpline(double* x_in, double* f_in, int size);
};
cSpline.cpp:
cSpline::cSpline(double* x_in, double* f_in, int size) {
f = f_in;
x = x_in;
sizex = size;
coeffs = setCoeffs(x, f, size);
}
double* cSpline::setCoeffs (double* x_in, double* f_in, int size) {
double *ypp = new double[size];
for (int i = 0; i < size; i++) {
ypp[i] = 0.0;
}
return ypp;
}
double* cSpline::interpolate(double* y, int sizey){
double *g = new double[sizey];
for (int i = 0; i < sizey; i++) {
g[i] = 0.0;
}
return g;
}
ss304.h:
#include "cSpline.h"
class SS304 {
private:
// Material constants
static const double T0 = 273.0; // standard temp, K
static const double rho0 = 7924.0; // density at STP, kg/m^3
static const double Tm = 1700.0; // melting temp, K
static const double Hm = 265.3+03; // heat of melting, J/kg
static const double rhom = 7015.0; // density of molten phase at Tm (iron), kg/m^3/K
static const double drdt = -0.883; // temperature coefficient of densiy at Tm, kg/m^3/K
static const double Tv = 3100.0; // vaporization temperature, K
static const double Hv = 6.258e+06; // heat of vaporization, J/kg
// Property Array Sizes
static const int Na1 = 10;
// Property Arrays
double alpha1[Na1]; //thermal expansivity, T0 < T < Tm, 1/K
double Talpha1[Na1];
cSpline csalpha1(double* x, double* f, int size);
public:
double* alpha;
void setProp1D(double* T, int size);
SS304();
};
ss304.cpp:
#include "ss304.h"
SS304::SS304() {
double alpha1[Na1] = {13.6e-6, 16.1e-6, 17.15e-6, 17.8e-6, 18.65e-6, 19.2e-06, 19.85e-06, 20.55e-06, 20.9e-06};
double Talpha1[Na1] = { 200., 400., 500., 600., 700., 800., 1000., 1200., 1400.};
cSpline csalpha1(Talpha1, alpha1, Na1);
}
void SS304::setProp1D(double* T, int size) {
double* alpha = new double[size];
alpha[0] = csalpha1.interpolate(T[0]);
}
What I am trying to accomplish here is this: Upon creation of a ss304 object, I set the properties of 304 stainless, in this case alpha1, at a given set of temperatures, Talpha1. Then I create a cSpline object, csalpha1, for later use. Creation of the cSpline object goes ahead and calculates the spline coefficients. Then, when I call SS304::setProp1D with an array of temperatures, it should set the values of alpha[] based on an interpolation of the cubic spline at each temperature in T[]. Obviously, I left out the full implementation of the spline for the sake of space, but the implementation is irrelevant to the error I get, which is:
ss304.cpp: In member function ‘void SS304::setProp1D(double*, int)’:
ss304.cpp:12: error: ‘((SS304*)this)->SS304::csalpha1’ does not have class type
So, I think I have some basic misunderstanding of how exactly classes work in C++. I think I am trying to use them as I do in Python, where I have this working just fine. But, obviously I am missing something in C++. I have googled around quite a bit, but not found any help that I understood. Any help would be greatly appreciated.
You can't initialize a data member in the constructor by calling the data member's constructor. You will need an assignment or initialization method for the object:
SS304::SS304()
{
static const double alpha1[Na1] = {13.6e-6, 16.1e-6, 17.15e-6,
17.8e-6, 18.65e-6, 19.2e-06,
19.85e-06, 20.55e-06, 20.9e-06};
static const double Talpha1[Na1] = {200., 400., 500.,
600., 700., 800.,
1000., 1200., 1400.};
csalpha1.initialize(Talpha1, alpha1, Na1);
}
Also, familiarize yourself with initialization lists and the C++ FAQ (which can be found by searching the web).
I converted the arrays into static const to force the compiler to place the data into Read-Only Memory and to access it from the Read-Only Memory (versus copying the data onto the stack).
The compiler will use a default or empty constructor when creating data members of a class.
static const double T0 = 273.0; // standard temp, K
static const double rho0 = 7924.0; // density at STP, kg/m^3
Wrong. In C++, static const double cannot be initialized in the class-definition itself. Only static integral type can be initialized inside the class.
Also, you cannot initialize member arrays in the constructor like you've done. That is also wrong. In fact, there is no way you can initialize them. However, you can do this:
SS304::SS304()
{
double local_alpha1[Na1] = {13.6e-6, 16.1e-6, 17.15e-6, 17.8e-6, 18.65e-6, 19.2e-06, 19.85e-06, 20.55e-06, 20.9e-06};
double local_Talpha1[Na1] = { 200., 400., 500., 600., 700., 800., 1000., 1200., 1400.};
std::copy(local_alpha1, local_alpha1 + Na1, alpha1);
std::copy(local_Talpha1, local_Talpha1+ Na1, Talpha1);
}
Now I would suggest you to read some good book first, because I feel that there are lots of basic things you need to know before writing classes in C++. Here is a list of some recommended books; choose any introductory book:
The Definitive C++ Book Guide and List
It seems to me that cSpline csalpha1(double* x, double* f, int size); is a function, so you should call it then call interpolate on the return value:
void SS304::setProp1D(double* T, int size) {
double* alpha = new double[size];
alpha[0] = csalpha1(..., ..., ...).interpolate(T[0]);
}
What you have is a function declaration:
cSpline csalpha1(double* x, double* f, int size);
I guess you wanted a member variable so use cSpline csalpha1;
I haven't coded in C++ in ages. And recently, I'm trying to work on something
involving structs. Like this
typedef struct{
int x;
int y;
} Point;
Then in a loop, I'm trying to create new structs and put pointers to them them in a list.
Point* p;
int i, j;
while (condition){
// compute values for i and j with some function...
p = new Point;
p* = {i, j}; //initialize my struct.
list.append(p); //append this pointer to my list.
}
Now, my question is it possible to simplify this? I mean, the pointer
variable *p outside of the loop and calling p = new Point inside the loop.
Isn't there a better/nicer syntax for this?
Sure:
Point * p = new Point;
You should probably also give your Point class a constructor:
struct Point { // note no need for typedef
int x;
int y;
Point( int ax, int ay ) : x( ax ), y( ay ) {}
};
so that you can say:
Point * p = new Point( i, j );
You may also want to make your list a list of Point values, rather than pointers, in which case you can avoid using dynamic allocation with new - always something to be avoided wherever possible in C++.
The struct can have a constructor like:
struct Point{
Point(int ax, int ay):x(ax), y(ay){}
int x;
int y;
};
and then the function can look like:
int i, j;
while (condition)
{
list.append(new Point(i,j));
}
As structs are classes with public members by default, you could even create a constructor within the struct and initialize your point object within the loop like this:
Point* p = new Point(i,j);
I would venture a guess that it is extremely unlikely you really need to allocate something like a Point dynamically.
Most likely you want to add a constructor and store them by value:
list<Point> list;
list.append(Point(x, y));
I recommend the Factory approach. Assuming that "Point" will be the base class of many objects, you can have a "Factory" that would return pointers.
Ex:
struct Point
{
Point(int mx, int my):x(mx),y(my) {}
int x;
int y;
};
// Circle, Polygon, etc.
class Factory
{
public:
static Point *getPoint(int mx, int my) { return new Point(mx, my); }
// Circle, Polygon, etc
};
Then in code someplace:
while(cond)
{
list.append(Factory::getPoint(i, j));
}