Planetary system simulation

The task is to use the concepts of OOP on the example of at least 5 classes that contain at least 4 functions: encapsulation, inheritance, polymorphism, overloading.

Am I on the right track?

I'm just learning, so I don't understand how you can use encapsulation and overload on the example of a planetary system? And how you can sort the sizes of the planets with reference to the planets themselves (so that you can then display the list on the screen).

#include <iostream>
#include <string>

using namespace std;

class Planet {
    private:
        std::string NamePlanet;
        std::string *ArrPlanet;
        const int sizePlanet;
        int NumberPlanet;
    public:
        Planet();
        ~Planet();
        void addPlanet();
        show();
};

class SpaceObject {
    private:
        std::string NameSpaceObj;
        std::string *ArrSpaceObject;
        const int sizeSpaceObj;
        int NumberSpaceObject;
    public:
        SpaceObject();
        ~SpaceObject();
        void addSpaceObj(); 
        show();         
};

class TypePlanet : public Planet {
    private:
        std::string NameTypePl;
    public:
        TypePlanet();
        ~TypePlanet();
        void addTypePlanet();
        show();
};

class SizePlanet : public Planet {
    private:
        int SizePl;
        int sizeSizePl;
        int *ArrSizePlanet;
        int NumberSizePl;
    public:
        SizePlanet();
        ~SizePlanet();
        void addSizePlanet();
        void SortSizePlanet();
        show();
};

class TypeSpObject : public SpaceObject {
    private:
        std::string NameTypeSpObj;
    public:
        TypeSpObject();
        ~TypeSpObject();
        void addTypeSpObj();
        show();
};

class SizeSpaceObject : public SpaceObject {
    private:
        int SizeSpObj;
    public:
        SizeSpaceObject();
        ~SizeSpaceObject();
        void addSizeSpaceObj();
        void SortSizeSpaceObj();
        show();
};

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

Planet::Planet(int maxSize) {
    sizePlanet(maxSize) {
        ArrPlanet = new Planet [sizePlanet];
        NumberPlanet = 0;
    }
}

Planet::~Planet() {
    delete [] ArrPlanet;
}

void Planet::add (Planet std::string NamePlanet){
    this->NamePlanet=NamePlanet;
    if (NumberPlanet < sizePlanet)
    ArrPlanet[NumberPlanet++] = NamePlanet;
}

SpaceObject::SpaceObject(int maxSize) {
    sizeSpaceObject(maxSize) {
        ArrSpaceObject = new SpaceObject [sizeSpaceObject]
        NumberSpaceObject = 0;
    }
}

SpaceObject::~SpaceObject() {
    delete [] ArrSpaceObject;
}

void SpaceObject::addSpaceObj(){
    if (NumberSpaceObject < sizeSpaceObject)
    ArrSpaceObject[NumberSpaceObject++] = value;
}

TypePlanet::TypePlanet() : Planet();

TypePlanet::~TypePlanet() : ~Planet();

void TypePlanet::addTypePlanet(TypePlanet std::string NameTypePl) : add (TypePlanet std::string NameTypePl)

SizePlanet::SizePlanet(int maxSize) {
    sizeSizePl(maxSize) {
        ArrSizePlanet = new SizePlanet [sizeSizePl];
        NumberSizePl = 0;
}

SizePlanet::~SizePlanet() {
    delete [] ArrSizePlanet;
}

void SizePlanet::addSizePlanet() {

}

void SizePlanet::SortSizePlanet() {
}
Author: Julien Jackson, 2019-01-28

2 answers

I support AR Hovsepyan in that:

The code should be written after you start to correctly represent what you want to get based on logical actions

Nevertheless, I was interested, and I decided to sketch a small example (using c++11) to clearly demonstrate what caused the author difficulties.

The essence of the subject area that came to my mind is graphics (game, visualization something).

So we have a planet. How can it be distinguished? Well, for example, each planet has a name, a radius, a mass. In addition (without going into the laws of gravity), the radius and mass will affect the acceleration of gravity on this planet. In addition, imagine that in our graphical application, the planet can explode, but explode in different ways (more on this below). As a result, we get the class Planet, which knows its properties, but does not know exactly how it will be explode.

Now let's highlight the types of planets or better politely look in Wikipedia and take for example the earth-type, gas and ice giants. They implement the function of the explosion in different ways, in theory substituting different animations of this explosion, different images, plus ice giants can explode with the scattering of blocks of ice. In practice, we get three child classes that look about the same, but differ by 1-2 words at the beginning of the output cout. As a result, we get the classes TerrestrialPlanet, GasGiantPlanet, IceGiantPlanet.

Next, imagine that the information about the planets that falls into the constructor Planet is located from the outside (DB, file, etc.) and that it has already entered the program, correctly parsed and written to the corresponding variables. Given this, we can write a stub function that will correctly create a planet, for example, by its name, and return a pointer to an object in the heap. As a result, we get the function Planet* getConcretePlanetByName(const string &);.

As a result, in the function main we get the opportunity to work with planet objects, without worrying about their type and proper initialization (thanks to polymorphism and inheritance) and create collections without fear of accidentally damaging the internal state of objects (thanks to encapsulation).

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

/*
 * Базовый класс планеты.
 */
class Planet
{
public:
    Planet(const string &name, double radius, double weight) : 
        name(name), 
        radius(radius),
        weight(weight)
    {
    }

    // виртуальный деструктор
    // если возникает вопрос "почему",то см. ссылку ниже
    virtual ~Planet() {}  

    // виртуальная функция взрыва, в которой и будет весь смак полиморфизма
    virtual void explode() = 0;

    // методы доступа 
    string getName() const { return name; }
    double getRadius() const { return radius; }
    double getWeight() const { return weight; }

    // если не ошибаюсь, это называется вычисляемым свойством
    double getAcceleration() const
    {
        const double G = 6.67e-11; // гравитационная постоянная
        return G * weight / (radius * radius);
    }

    // если я правильно понял автора на счет перегрузки
    // положим, что планета будет больше другой планеты, если больше ее радиус
    const bool operator > (const Planet &right)
    {
        return radius > right.radius;
    }

// ну и соответственно поля приватные для обеспечения инкапсуляции
private:
    string name;
    double radius;
    double weight;
};


/* 
 * Классы, реализующие разные типы планет.
 */

class TerrestrialPlanet : public Planet
{
public:
    using Planet::Planet;

    void explode() override
    {
        cout << "Terrestrial planet " << getName() << " was exploded with acceleration = " << getAcceleration() << endl;
    }
};

class GasGiantPlanet : public Planet
{
public:
    using Planet::Planet;

    void explode() override
    {
        cout << "Gas giant planet " << getName() << " was exploded with acceleration = " << getAcceleration() << endl;
    }
};

class IceGiantPlanet : public Planet
{
public:
    using Planet::Planet;

    void explode() override
    {
        cout << "Ice giant planet " << getName() << " was exploded with acceleration = " << getAcceleration() << endl;
    }
};


/*
 * Функция заглушка, в которой уже есть все готовые 
 * данные для инициализации объектов 
 */
Planet* getConcretePlanetByName(const string &name)
{
    // все числа взяты из гугла, радиус в метрах, масса в килограммах
    double earthRadius = 6371000;
    double earthWeight = 5.97e24;

    double jupiterRadius = 69911000;
    double jupiterWeight = 1.89e27;

    double neptuneRadius = 24622000;
    double neptuneWeight = 1.02e26;

    if (name == "Earth")
        return new TerrestrialPlanet("Earth", earthRadius, earthWeight);
    if (name == "Jupiter")
        return new GasGiantPlanet("Jupiter", jupiterRadius, jupiterWeight);
    if (name == "Neptune")
        return new IceGiantPlanet("Neptune", neptuneRadius, neptuneWeight);

    return nullptr;
}

/*
 * И самое вкусное - функция main:
 */
int main()
{
    vector<Planet*> planets {                // какой-то набор планет
        getConcretePlanetByName("Earth"),
        getConcretePlanetByName("Jupiter"),
        getConcretePlanetByName("Neptune")
    };

    sort(planets.begin(), planets.end(),     // сортировка
        [](Planet* a, Planet* b) -> bool
    { 
        return *a > *b;
    });

    for (auto &planet : planets) {            // полиморфизм
        planet->explode();
        delete planet;
        planet = nullptr;
    }

    return 0;
}

As a result, we get the following conclusion:

Gas giant planet Jupiter was exploded with acceleration = 25.7927                                                                                                                  
Ice giant planet Neptune was exploded with acceleration = 11.2222                                                                                                                  
Terrestrial planet Earth was exploded with acceleration = 9.81036

What we have as a result:

  1. Encapsulation. Users of the class do not have the ability to change the field valuesname, radius, weight. If they have this opportunity, it's sooner or later it could lead to changes in these fields and, as a result, the getAcceleration() function working incorrectly (someone accidentally, for example, in the constructor of a child class, purely for the good reasons "initialize the variable before using" would do radius = 0.. How would the function work then?).
  2. Inheritance. The classical implementation of the relation IS-A from the abstract to the more specific (Terrestrial planet is a planet). Combines semantic and mechanical inheritance.
  3. Polymorphism. The ability to call the interface function explode for each specific type, without worrying about which type is used.
  4. Overload. Operator overloading is used to simplify the object comparison syntax.

In a good way, it would not hurt to play with the accuracy of calculations. Once I took up overloading comparison operators, it would also not hurt to bring it to mind. Check on nullptr, too, would not hurt at the baptism of fire. But I think it is quite suitable for a demo example.

P.S. the promised link to information about the virtual destructor: Myers Scott-Effective use of C++

 3
Author: Bogdan, 2019-01-28 20:48:53

The fact is that you need to write the code after you start to correctly represent what you want to get based on logical actions.

If a planet has a name, then it must be a single class (singleton\, since there is only one planet with a specific name. In this case, you must define a structure (class) for the properties of the planets, and each planet must have a specific Planet_traits object (you must write this class) and other data (for example, a static object_star, around which it revolves...

If you decide to use inheritance, then the planet should reflect the general concept, and each derived class should reflect planets with a specific property. For example: Gas_giants, dwarf planets, etc. At the same time, the abstraction depends entirely on who you want to look at them from. For astronomers, one abstraction, for the average person, another (he can only be interested in the distance from the earth, whether there is life or not, and whether it is available to our eyes...

All of this is called architecture, and you've chosen a slightly complicated topic for a beginner (or for a single programmer).

You need to encapsulate the data that the client does not need. Those functions that are performed differently for each type of planet need to be made virtual, and then you will have polymorphism.

 2
Author: AR Hovsepyan, 2019-01-29 04:25:01