Jump to content
  • Advertisement
Sign in to follow this  
Odion

Beginner Question - Good Practice

This topic is 832 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Edited by Odion

Share this post


Link to post
Share on other sites
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! :) Edited by braindigitalis

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
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?

Edited by Alberth

Share this post


Link to post
Share on other sites

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: 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!