1

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?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Gilfoyle
  • 3,282
  • 3
  • 47
  • 83
  • What did the old C style arrays look like? – John Go-Soco Oct 25 '19 at 08:36
  • Have you tried `: n{n_}, dt{dt_}, t_max{t_max_}, p{n} {`? – Krzysiek Karbowiak Oct 25 '19 at 08:36
  • @JohnGo-Soco I used `Particle *p = new Particle[n];` in the private part of my class and was told that this is really bad. – Gilfoyle Oct 25 '19 at 08:38
  • For the same reasons you should consider replacing the arrays in `Particle` with `std::array` – super Oct 25 '19 at 08:42
  • @Samuel did you ask why it was bad? – Roy2511 Oct 25 '19 at 08:43
  • Just out of curiosity, why are `x`, `v`, and `F` in `Particle` being represented as arrays? It's like a single particle can have multiple positions, velocities, and forces. – John Go-Soco Oct 25 '19 at 08:46
  • @Roy2511 First I placed `Particle *p = new Particle[n];` inside `private` which would lead to undefined behaviour. – Gilfoyle Oct 25 '19 at 08:46
  • 1
    @JohnGo-Soco I guess it's like x,y,z ; Vx,Vy,Vz; Fx,Fy,Fz ... Each particle's representation in space, the instantaneous velocity vector it has and force vector applied on it – Roy2511 Oct 25 '19 at 08:49
  • @Roy2511 Ah, good point. Might be better if that was explicitly three different space variables rather than an array then, unless this is for n-dimensional particles? – John Go-Soco Oct 25 '19 at 08:57
  • Careful with uniform initialisation and `std::vector`: `std::vector v{1, 7}` will *not* produce a vector with two elements, but one with 7, as there's a constructor overload accepting two parameters. This is the most important reason I *personally* (feel free to be of opposite opinion!) consider UI broken (there are quite a few minor other examples). `std::vector v(1, 7)` vs. `std::vector v({1, 7})` is just so much clearer (and yes, initialiser lists *are* great...). – Aconcagua Oct 25 '19 at 12:31
  • @super Can you explain why? Should I also consider taking `std::vector()`? – Gilfoyle Oct 25 '19 at 20:03
  • @Samuel For a dynamic array use `std::vector`, for a static array use `std::array`. Take a look at [cppreference](https://en.cppreference.com/w/cpp/container/array) and you will see some of the handy member function it's equipped with. It also has overloads for a lot of free functions like `std::begin` ect. And what makes it great is that it's still just a static array, nothing more. – super Oct 25 '19 at 20:22

3 Answers3

6

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/

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

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; 
};
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

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.

user253751
  • 57,427
  • 7
  • 48
  • 90