Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objects storing data and objects storing smart pointers to data

Tags:

c++

Let's say I want to organise fleets in my galactic empire.

class Fleet
{
    string m_commander;
    int m_totalShips;
}


class GalacticEmpire
{
    string m_coolName;
    string m_edgyTitleOfTheRuler;
    /*Problem line number1*/
}

GalacticEmpire is an object that will own and control everything that is solely of my empire - it will store all the bytes of its data.

But my GalacticEmpire does not exist in void (I mean technically it does), there are StarSystems it controls.

class StarSystem
{
    string m_name;
    color m_starColor;
    /*Problem line number2*/
}

Now StarSystem is not supposed to store any raw data - it only has to be able to point to Fleets that station within its bounds.

And now the question.

How GalacticEmpire should store Fleets so that it can access them and destroy them (entirely from program) and simultaneosly how StarSystem should be able to point to Fleetss so that it can iterate over them? In the other words, how should Problem line number1 and Problem line number2 look like?

Option #1:

Problem line number1 = vector<Fleet> m_fleets

Problem line number2 = vector<shared_ptr<Fleet>> m_fleets

Option #2:

Problem line number1 = Problem line number2 = vector<shared_ptr<Fleet>> m_fleets

like image 820
Grynshphvil Avatar asked Sep 19 '19 19:09

Grynshphvil


People also ask

What are objects and pointers?

A pointer is a type of variable that carries location information. In this case, the example variable will store the address of an Order object that we want to interact with. We initialize the pointer variable by using the C++ new operator to construct a new object of type Order.

What are smart pointers used for?

Smart pointers are used to make sure that an object is deleted if it is no longer used (referenced). The unique_ptr<> template holds a pointer to an object and deletes this object when the unique_ptr<> object is deleted.

What is pointer smart pointer and shared pointer explain using diagram and program?

It is a container of raw pointer and a reference counting (a technique of storing the number of references, pointers or handles to a resource such as an object, block of memory, disk space or other resources) ownership structure of its contained pointer in cooperation with all copies of the shared_ptr.


1 Answers

That seems to be an interesting exercise in object design. Let's try something out naively.

class GalacticEmpire
{
    std::string m_coolName;
    std::string m_edgyTitleOfTheRuler;
    std::vector<Fleet> fleets;
};

That seems right - Empire owns it's own fleets, they are arranged in the container (vector), and we do not need to use any indirection here - vector stores Fleet objects.

Now, let's use view pointer in the StarSystem:

class StarSystem
{
    string m_name;
    color m_starColor;
    std::vector<Fleet*> fleets;
}

We will populate StarSystem::fleets with addresses from GalacticEmpire::fleet, and it seems to work at the first glance.

Unfortunately, this solution is extremely brittle. If Empire happens to add new fleets to it's force, it would do this by adding objects to GalacticEmpire::fleets vector and will invalidate the addresses to those stored in StarSystem::fleets. Not great!

Second attempt:

 class GalacticEmpire
 {
    // ...
    std::vector<std::unique_ptr<Fleet>> fleets;
 };

And StarSystem::fleets store (non-owning) pointer managed by unique_ptrs of GalacticEmpire::fleets. This solves us a problem of adding new fleets - even if pushing new elements to the vector ivalidates pointers to unique_ptrs, the pointers managed by said ptrs remain valid.

However, this solution has two drawbacks: you loosing performance. Objects which could be stored directly in the vector of fleets are now created dynamically. Accessing those requires indirection, and it all takes a heavy toll on performance.

The other problem is logical - we have solved a problem of adding new fleet, but what if the fleet is removed? We do need to clean-up this fleet from the StarSystem it expected to be!

Let's think for a moment. It is clear that a StarSystem can host multiple fleets, but a fleet can only be stationed within a single StarSystem. Let's use this information:

class Fleet
{
    string m_commander;
    int m_totalShips;
    StarSystem* stationed_system;
};

We add the pointer to the StarSystem this fleet is hosted to the fleet itself. Now, when an Empire looses one of it's fleets, we should be able to clear that fleet from the list of fleets stationed in the StarSystem. But, how do we find it? Iteratively in the vector? This is rather slow. Let's do unordered_set instead, so we will be able to find (and remove) a fleet in constant time!

class StarSystem
{
    std::unordered_set<Fleet*> fleets;
};

Now, the only thing left is to make sure we introduce a certain type of friendship between classes and add some private and public functions which would guarantee that anytime a fleet is removed it is also removed from it's StarSystem. This is left for the reader.

like image 88
SergeyA Avatar answered Oct 26 '22 13:10

SergeyA