Extending our Solar System Class

Extending our Solar System Class#

Let’s make our class more useful. Lets implement the following functions:

Planet* get_planet(const std::string& name);

double get_period(const std::string& name);

The first will take the name of a planet, and if it exists in our SolarSystem, it will return a pointer to the Planet. If the planet does not exist, then the pointer will be nullptr.

The second will take a planet name and compute and return its period.

Here’s our updated class:

Listing 114 new solar_system.H#
#ifndef SOLAR_SYSTEM_H
#define SOLAR_SYSTEM_H

#include <vector>
#include <cassert>
#include <cmath>
#include <algorithm>

#include "planet.H"

struct SolarSystem {

    double star_mass;
    std::vector<Planet> planets;

    SolarSystem(const double mass)
       : star_mass{mass}
    {
        assert(mass > 0.0);
    }

    void add_planet(const std::string& name,
                    const double a, const double e=0.0) {

        // make sure a planet with that name doesn't already exist
        // if the pointer returned here is not null, then the planet exists

        if (std::ranges::any_of(planets,
                                [=] (const Planet& p) {return p.name == name;})) {
            std::cout << "planet already exists" << std::endl;
            return;
        }

        Planet p{.name=name, .a=a, .e=e};
        planets.push_back(p);
    }

    void print_planets() {

        for (const auto& p : planets) {
            std::cout << p << std::endl;
        }
    }

    Planet* get_planet(const std::string& name) {

        auto it = std::ranges::find_if(planets,
                                       [&] (const Planet& p) {return p.name == name;});

        if (it != planets.end()) {
            return &(*it);
        }

        return nullptr;
    }

    double get_period(const std::string& name) {

        double period{-1.0};

        const auto *p = get_planet(name);

        if (p) {
            period = std::sqrt(std::pow(p->a, 3.0) / star_mass);
        }

        return period;
    }

};
#endif

Some notes:

  • We’ve rewritten our add_planet check on whether the planet already exists to use std::ranges::any_of. This will return true if we already have a planet with that name. This makes the code more compact.

  • Our get_planet function uses std::ranges::find_if to find the planet with the name we provide. This returns an iterator that points to the planet. If the planet does not exist, then the iterator will point to one-past the last element, planets.end().

    We want to return a pointer to the Planet in the vector, and not the iterator, so we do:

    return &(*it);
    

    This first dereferences the iterator, *it, to get the underlying Planet, and then it takes the address of that, using the & operator.

and here’s a driver:

Listing 115 new test_solar_system.cpp#
#include <iostream>
#include "solar_system.H"

int main() {

    SolarSystem ss(2.0);

    ss.add_planet("alpha", 1.0);
    ss.add_planet("beta", 1.5, 0.1);
    ss.add_planet("gamma", 3.0, 0.24);

    // this generates an error
    ss.add_planet("alpha", 2.0);

    std::cout << "period of alpha = "
              << ss.get_period("alpha") << std::endl;

}

try it…

To understand the difference between a struct, where everything is public, and a class where everything is private by default, let’s edit solar_system.H and change struct to class.

What happens when we compile?

Now try adding a public: statement to the code.