Beginner Question - Good Practice

Started by
14 comments, last by Odion 7 years, 8 months ago

Hi all,

I am kinda stuck at a simple battle simulator game I am programming. Id appreciate someone more experienced helping me out.

The question is:

I have a system something like this:

int main() ----> mapManager.loadMap(), mapManager.prontMap(), ....... , mapManager.battle()

MapManager has a soldiers vector, which holds all the fighting Soldier objects for both armies

Soldier class handles the invidual AI (A star algorithm)

My question is, how can I reach the soldiers vector (which is in MapManager class) from my Soldier class.

The only solution I see: Since I created the MapManager mapManager object in main(), I have to pass the pointer of that object to battle(mapManager) -> Soldier.update(mapManager) so I can access mapManager.soldiers in my Soldier::update(). But this feels really bad practice.

How should I do this? Is there a way to directly reach the mapManager.soldiers vector without passing that one object through a pipeline of functions?

Thank you in advance!

Odion

Advertisement
It's not bad practice to pass pointers to various objects to other objects.

Consider your objects as a tree of elements each one containing one or more of another type (this is the definition of composition rather than inheritence).

For each object that is owned by another, pass in only what is relevant as constructor values, and store copies of the pointer. For example:
// A soldier contains various soldier-like methods
class Soldier
{
protected:
    Map* creator;
public:
    Soldier(Map* m) : creator(m)
    {
        // [ .. ]
    }

    void Navigate()
    {
        Game* g = creator->GetGame();
        // [ call methods on 'g'... ]
    }
}
 
// A map contains a reference to its game, and contains an instance of a soldier
class Map
{
protected:
   Game* creator;
   Soldier* sold;
public:
   Map(Game* g) : creator(g)
   {
        sold = new Soldier(this);
   }

   // Accessor used by Soldier etc
   Game* GetGame()
   {
       return creator;
   }
}

// Game contains an instance of a map
class Game
{
protected:
    Map* currentmap;
public:
    Game()
    {
        // Construct Map, passing in 'Game'
        currentmap = new Map(this);
    }

    void Run()
    {
        // Example... Does nothing.
        for(;;);
    }
}

int main()
{
    // Only use pointers where it makes sense!
    Game g;
    g.Run();
    return 0;
}



Note that this example doesn't take into consideration proper ownership of pointers e.g. with smart pointers etc. This is something you should also make use of as best practice where possible.
This example also doesn't have destructors so it leaks ram. Just take this as an incomplete example, to get my point across.
So long as you ensure that whatever you instantiate can only see what it needs to see, you don't end up with a mess. Keep your classes and objects organised neatly.

Also, i'm not sure the soldier vector even belongs in the manager? "Managers" are generally a "code smell", wherever you have a manager consider naming it more effectively, and if you can't name it effectively this is a sign the class is doing too many things. Split that class up into smaller classes, each of which does less.

Hope this helps! :)

Wow this was very instructional. Thank you! I learned a lot.

You didn't use point for Game g because it is too small so it can stay in stack memory, right?

I heard in a tutorial that smart pointers have a 8-9 times performance overhead, so it is better to use raw pointers and delete memory allocations manually.

I made a buggy half-game in GameMaker, but I it didn't teach me much about programming in general.

I am still trying to figure out the optimal structure (class tree, class names, objects) of the overall code. But I think I a tiny step closer now.

Thank you very much!

Odion

Wow this was very instructional. Thank you! I learned a lot.
You didn't use point for Game g because it is too small so it can stay in stack memory, right?

I heard in a tutorial that smart pointers have a 8-9 times performance overhead, so it is better to use raw pointers and delete memory allocations manually.

I made a buggy half-game in GameMaker, but I it didn't teach me much about programming in general.

I am still trying to figure out the optimal structure (class tree, class names, objects) of the overall code. But I think I a tiny step closer now.
Thank you very much!
Odion


The only reason I didn't use a pointer for g was because there was no real need. It is allocated on the stack as I know that when main ends it will automatically be destructed and freed without need to manually manage a pointer. Wherever you can avoid a manually managed pointer it helps with readability and stability as you just avoided a potential memory leak and a host of other problems.

I'm not sure about pointers being less efficient though and would take that with a pinch of salt.

Hope this helps!

I see. Great! Thanks a lot for helping me out!

Pointer deferencing is effectively free if you're not getting goofy with multiple indirection in a tight loop. The CPU has an address lookup unit that does any needed pointer math in the pipeline before the address is actually needed. As long as you don't overload that unit (which is actually hard to do) you should never see a performance impact just from dereferencing.

You may be thinking of cache coherency, which is a matter of how you traverse memory rather than how you reach the memory you're traversing. That can be impactful for things like linked lists because over time the list may end up referring to objects that are scattered all over memory, which means that the cache can become worthless and you have to wait on the bus for every node that misses, which is vvveeerrryyy ssslllooowww. Using a container like a vector is very fast in comparison because the data is contiguous in memory, which means that the next element is very likely to be in the cache where it can be accessed quickly and easily.

Traversing a linked list can also be expensive because looking through a pointer to find a pointer to find a pointer (etc) can indeed overload the lookup unit.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Interesting. Lot of helpful info thx!

However I meant smart pointers (which deletes itself when the block falls of the stack eg.: shared pointer) being 8-9 times more performance heavy, than manually handled pointers

However I meant smart pointers (which deletes itself when the block falls of the stack eg.: shared pointer) being 8-9 times more performance heavy, than manually handled pointers

The question is thus, can you implement a proper solution in managing the memory that is less than 8-9 times as heavy as "practically free"? (9 times almost nothing is still almost nothing.)

If you can, is it worth your time coding the solution?

I don't know where you got these 8-9 times as heavy from, but typically these numbers are created by people that want to prove plain pointers are better, or shared pointers are bad. Obviously, there is going to be a difference, since a plain pointer doesn't handle memory management like a shared pointer does. To get a fair comparison, you need to factor in the time required to handle memory management yourself, both in your development time, and in terms of cpu time.

There is a second aspect here, namely how serious is this wasted time.

Suppose it's 0.1% of your total program execution time. (This would be really bad, I would expect it much smaller.) If you can completely eliminate this cpu time, it means you gain 0.1% cpu time here. So instead of 100%, you now have 99.9% program execution time. (that's saving 29 seconds on 8 hours cpu time).

Suppose there is some other part in the code that takes like 20%. Suppose you can eliminate 1/10th of it, 2% of the total time (eliminating a small part is much easier than completely eliminate some part!). This means you drop from 100% to 98%. That's 576 seconds on 8 hours cpu time, or 9.6 minutes saved.

Do that for 2 other pieces of code that need 20% of the cpu, and you gain almost 1/2 hour right there.

I ask you, where do you want to spend your time on? Optimizing pointer memory managing which gains very little compared to the overall code, or finding a few bigger fish that save you much more cpu time with less effort?

Obviously the latter, as you described. I totally agree. Btw here is my source for that smart pointer overhead info, tho, he did his own tests:

However I meant smart pointers (which deletes itself when the block falls of the stack eg.: shared pointer) being 8-9 times more performance heavy, than manually handled pointers

Pics or it didn't happen.

Obviously the latter, as you described. I totally agree. Btw here is my source for that smart pointer overhead info, tho, he did his own tests:

Saying out loud on an internet video "this is eight to nine times slower" does not constitute testing, even if you say, "after all, I'm more like a C programmer" at the end.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

This topic is closed to new replies.

Advertisement