c++ question about stack , heap and dereferencing

Started by
13 comments, last by Zahlman 16 years, 1 month ago
This is probably a stupid question as I think I know what the answer is but I want to make sure: By declaring an object with the "new" operator, which creates something on the heap, does that mean that all variables AND pointers contained in it are also on the heap? If so... another question: In my game right now, I have a lot of pointer dereferencing this->getThat()->getSomething()->getAnother()->youguessedit(); No, that is not an exaggeration. Is this really slow? And if it is, do I gain any speed by not creating pointers in objects that I know will be created on the heap anyway? So I would end up using: this->that().something().another().youguessedit(); ...Or is it the same thing?
Advertisement
Quote:
By declaring an object with the "new" operator, which creates something on the heap, does that mean that all variables AND pointers contained in it are also on the heap?

More or less, yes. There are nitpicky little details and the occasionally obscure edge case, but generally once you put something on the heap, it and its subobjects are on the heap.

Quote:
Is this really slow? And if it is, do I gain any speed by not creating pointers in objects that I know will be created on the heap anyway? So I would end up using:
this->that().something().another().youguessedit();

...Or is it the same thing?

There are number of things wrong with this technique. Speed is one of them -- or rather, decreases in technical speed are a side-effect of the poor cache locality of the objects you are likely accessing. That is to say, all those pointers you're following may be pointing to all sorts of different locations in memory, and jumping around like that isn't particularly optimal.

However, the speed penality is probaby small -- small enough that, if this were a good, correct way to access your objects, it would not be worth changing.

However, it's not a good way to access your objects. You aren't doing any checks for null pointers, for example. If the objects can't ever be null, you should perhaps be returning references instead.

How you expose the properties (return them) is not neccessarily the same as how you store them (you could store pointers to Foo inside the class, but return a reference to Foo from GetFoo()). Expose them according to how they should be used -- generally references if they cannot be null -- and store them according to how they should exist. If a particular field of an objects is always present, there's usually no reason for that field to be a pointer to heap storage.

In general, it's usually better to avoid pointers unless you need the behavior a pointer can provide you.

A final note is that this kind of deep pointer chasing is usually symptomatic of poor design: it suggests that you are doing a lot of a processing related to the tail-end of the pointer chain at a higher level point (the base-end of the pointer chain) and thus that your objects are probably little more than "dumb" containers for data with a bunch of naive get/set methods.
when you create an object on the heap the whole object is on the heap, so yes to the first question. Each pointer dereference is a memory access and as such is a performance hit but slow no not really. not using pointers means those functions could be inlined. The main question I would be asking myself though is why is it necessary to hop through three or four references to reach something. It sounds like you may have over-engineered and now have containers of containers of containers.
Quote:Original post by jpetrie

However, it's not a good way to access your objects. You aren't doing any checks for null pointers, for example. If the objects can't ever be null, you should perhaps be returning references instead.


Well most of them can be null, when they are deleted.

Quote:Original post by jpetrie
In general, it's usually better to avoid pointers unless you need the behavior a pointer can provide you.


The reason I did this was to make damn sure my objects would be stored on the heap as I would have lots of them and didn't want to run out of stack memory.

Quote:Original post by jpetrie
A final note is that this kind of deep pointer chasing is usually symptomatic of poor design: it suggests that you are doing a lot of a processing related to the tail-end of the pointer chain at a higher level point (the base-end of the pointer chain) and thus that your objects are probably little more than "dumb" containers for data with a bunch of naive get/set methods.


Some are. I have an object which stores arrays of ships, bullets, asteroids etc... This is so I can do collision detection among my ships, and render lists of objects. Then each ship stores SDL_Surfaces (basically images) along with smaller objects such as guns, which also store SDL_Surfaces, about 32 each (SDL can't rotate images, I have to preload them).

My objects do more than just hold data:
When I call .Move() on my Ships, they will also set the angle and location (in the world) of their guns. That location is used by the render function to display things relative to each other on my screen.

But even if they were just dumb containers... isn't that what OOP is all about? organizing things in containers?

Another question about speed: Since my objects contain a lot of data that might be useless in some functions, for example, collision detection. I do plan on doing pixel-perfect detection at some point which means I will need the surfaces, but does making a call on a huge object take longer?

i.e "Object A has 1000 other objects (or pointers to them, if its the same thing for this purpose). Object B has only 5 other objects. If I make a call like ObjectA.getX vs. ObjectB.getX (where X is considerably smaller than the other members) will there be a difference? If so, how big, and why is there a difference? In other words when I make a call on an object does the whole object get copied somewhere, or only the part of it that I am trying to access?

Quote:
The reason I did this was to make damn sure my objects would be stored on the heap as I would have lots of them and didn't want to run out of stack memory.

This is a premature optimization. It's surprisingly difficult to store too much information on the stack "accidentally." You don't need to make everything a pointer to ensure that, either, and you shouldn't. Remember that which region of memory an object ends up living in isn't as system as "pointer == heap, no pointer == stack." It also has to do with the location of the containg object and that object's declaration and initialization.

Quote:
Some are. I have an object which stores arrays of ships, bullets, asteroids etc... This is so I can do collision detection among my ships, and render lists of objects. Then each ship stores SDL_Surfaces (basically images) along with smaller objects such as guns, which also store SDL_Surfaces, about 32 each (SDL can't rotate images, I have to preload them).

There should be a separation between logic and presentation; if Ship contains any sort of game logic (a measure of life left, how much bullets the ship has, what weapons its carrying, how to fire the gun, et cetera) it is a logic object and should not have anything to do with rendering. Logic should drive rendering -- that is, tell the rendering subsystem what to draw based on current state -- but they should not, ideally, be mixed together.

Quote:
But even if they were just dumb containers... isn't that what OOP is all about? organizing things in containers?

Not at all. Object oriented design is about building complete representations that model reality in some way -- "objects" with understanding of both state and behavior. When you externalize your behavior, you start moving to the realm of procedural programming.

Furthermore, good OO design has a lot to do with isolating responsibily, scoping that responsibility to the smallest scope that makes sense. In your case, you seem to have an exeedingly large amount of responsibility tucked away at the high-level end of your objects, and almost no functionality at the low end. This gives the higher-level objects more responsiblity, violating the principle of single responsibility that helps make OO designs cleaner, more maintainable, and reusable.

Quote:
Another question about speed: Since my objects contain a lot of data that might be useless in some functions, for example, collision detection. I do plan on doing pixel-perfect detection at some point which means I will need the surfaces, but does making a call on a huge object take longer?

No, a member function call is, internally, usually just a call to a regular function that takes the first parameter ("this") that is a pointer to the object that function is being invoked on. The size of that object is irrelevant.
Quote:Original post by finky45
The reason I did this was to make damn sure my objects would be stored on the heap as I would have lots of them and didn't want to run out of stack memory.


This is generally faulty thinking. Consider, for example:

class Foo {  Bar x, y, z;};


If we put a Foo on the heap, then its contained Bars are also all on the heap, because they are, well, contained. Baked right into the memory structure of the Foo object.

Quote:My objects do more than just hold data:
When I call .Move() on my Ships, they will also set the angle and location (in the world) of their guns.


Good.

Quote:That location is used by the render function to display things relative to each other on my screen.


This depends on how you do it, though. A very powerful guiding principle for OO is "tell, don't ask", aka dependency inversion.

Quote:But even if they were just dumb containers... isn't that what OOP is all about? organizing things in containers?


OO is about breathing life into the "things". Organization is just good practice.

Quote:i.e "Object A has 1000 other objects (or pointers to them, if its the same thing for this purpose). Object B has only 5 other objects. If I make a call like ObjectA.getX vs. ObjectB.getX (where X is considerably smaller than the other members) will there be a difference? If so, how big, and why is there a difference? In other words when I make a call on an object does the whole object get copied somewhere, or only the part of it that I am trying to access?


There is no such thing as partially copying an object. However, calling a function on an object does not involve copying it. Assuming getX() just returns an 'x' member of the object, what conceptually happens is that some known offset is added to the memory location of the object, and a type-of-x-sized chunk of memory is retrieved from that location.



Let's try an example.

// Suppose we have some declarations like (of course I am omitting lots of stuff)class Renderer {  SDL_Surface* screen;  public:  // Draw the region (x, y, w, h) of the surface onto screen, at (xpos, ypos).  void drawSpriteAt(int xpos, int ypos, int x, int y, int w, int h, const SDL_Surface* surface) const;};class Level {  std::vector<Enemy*> enemies;  public:  Enemy* getEnemy(int index) const;};class Enemy {  SDL_Surface* surface;  SDL_Rect rect; // the portion of the surface that illustrates this enemy  SDL_Point position;  public:  SDL_Surface* getSurface() const;  SDL_Rect& getRect() const;  SDL_Point& getPosition() const;};Level* level;Renderer* renderer;// bad in many waysrenderer->drawSpriteAt(level->getEnemy(i)->getPosition().x, level->getEnemy(i)->getPosition().y, level->getEnemy(i)->getRect().x, level->getEnemy(i)->getRect().y, level->getEnemy(i)->getRect().w, level->getEnemy(i)->getRect().h, level->getEnemy(i)->getSurface());


On the surface, it seems like we're doing a few things correctly: we have accessors instead of just looking at data members directly, and the code has been written to be const-correct. However, we're actually making things hugely more complicated than they need to be, and exactly wrongly organized.

The first things we can do are to get rid of the unnecessary indirection (keep in mind that even if we put all *our* stuff on the stack, the really *big* stuff - i.e., the data pointed at by the SDL_Surface*s - will be put on the heap by SDL), and use a reference to clean up the repeated access pattern.

class Renderer {  SDL_Surface* screen;  public:  // Draw the region (x, y, w, h) of the surface onto screen, at (xpos, ypos).  void drawSpriteAt(int xpos, int ypos, int x, int y, int w, int h, const SDL_Surface* surface) const;};class Level {  std::vector<Enemy> enemies;  public:  Enemy& getEnemy(int index) const;};class Enemy {  SDL_Surface* surface;  SDL_Rect rect;  SDL_Point position;  public:  SDL_Surface* getSurface() const;  SDL_Rect& getRect() const;  SDL_Point& getPosition() const;};Level level;Renderer renderer;// Improved slightlyEnemy& e = level.getEnemy(i);SDL_Point& point = e.getPosition();SDL_Rect& rect = e.getRect();renderer->drawSpriteAt(point.x, point.y, rect.x, rect.y, rect.w, rect.h, e.getSurface());


Now the function call is a lot more readable: we can immediately see what each parameter is for. But it's still rather complicated: why not just pass the structures for position and sprite-rect, and let drawSpriteAt do the unpacking? (I could have made the first example worse: imagine separate accessor functions for x, y, w, h of the rect for example!)

But that is still missing the point: we are reaching into the Enemy structure to do the job of drawing it. That puts the responsibility in the wrong place. The Enemy is the object with the information that's needed for drawing, so the Enemy should assume the responsibility for that draw call. We apply that tell, don't ask principle, and give the Enemy the power to draw itself, and remove the now-unneeded accessors.

class Renderer {  SDL_Surface* screen;  public:  // Draw the region rect of the surface onto screen, at p.  void drawSpriteAt(SDL_Point p, SDL_Rect rect, const SDL_Surface* surface) const;};class Level {  std::vector<Enemy> enemies;  public:  Enemy& getEnemy(int index) const;  int enemyCount() const;};class Enemy {  SDL_Surface* surface;  SDL_Rect rect;  SDL_Point position;  public:  // This time, I'll show the implementation:  void drawTo(const Renderer& r) {    r.drawSpriteAt(position, rect, surface);  }};Level level;Renderer renderer;// Improving bit by bit... now let's see the whole loop:for (int i = 0; i < level.enemyCount(); ++i) {  level.getEnemy(i).drawTo(renderer);}


Wow, much shorter this time! But we can still improve, by repeating the technique for the Level. It knows what all the enemies are; why ask it continually for each enemy in order to draw them? The same principle applies: the level can do the drawing.


class Renderer {  SDL_Surface* screen;  public:  // Draw the region rect of the surface onto screen, at p.  void drawSpriteAt(SDL_Point p, SDL_Rect rect, const SDL_Surface* surface) const;};class Level {  std::vector<Enemy> enemies;  public:  void drawAllEnemies(const Renderer& r) const {    // Because we're not "squeezed through the interface" of another class now,    // we can do the loop in a more natural way:    std::vector<Enemy> end = enemies.end();    for (std::vector<Enemy> it = enemies.begin(); it != end; ++it) {      it->drawTo(r);    }  }};class Enemy {  SDL_Surface* surface;  SDL_Rect rect;  SDL_Point position;  public:  // This time, I'll show the implementation:  void drawTo(const Renderer& r) {    r.drawSpriteAt(position, rect, surface);  }};Level level;Renderer renderer;level.drawAllEnemies(renderer);


Beautiful. Now all the code is in the right place. Only one more change I want to make: the Enemy class presumably has a lot of other stuff in it besides the data for drawing the critter, and there are presumably lots of other things in the game that will need to be drawn the same way. We don't want to have to duplicate this setup in other classes, so it makes sense to extract a Sprite struct, and give the drawTo() function to that. Then the Enemy simply holds a Sprite, and delegates to it for drawing:

class Enemy {  Sprite s; // looks very much like the old Enemy above :)  public:  void drawTo(const Renderer& r) {    s.drawTo(r);  }};


Now we can reuse that in other classes. Instead of duplicating the set of data members and the drawTo implementation, we just duplicate this bit of delegation (and we don't even need that, if we make clever use of inheritance; but be careful - composition and delegation is often more flexible and avoids subtle design problems).
Quote:Original post by Zahlman
OO is about breathing life into the "things".

That sounds quite dramatic.
Wow Zahlman, that's like a whole mini-tutorial ^^. I've always been a little confused about a nice way to keep code organized, readable and "OOP". Your examples made it pretty clear to me and seem like a nice guideline (so I bookmarked this thread). Thank you ^^!

Sorry if this is seen as an annoying off-topic post.. But I just wanted to mention it.
Thanks Zahlman,

Looks I have to change a few things in my code now, and redesign part of it at least.
Got another question for Zahlman:

In my game, the player will always be in the middle of the screen. Consequently where I draw the enemies depends on where the player is in the level.

I don't know how I can implement this while keeping my code object oriented.

If I tell the enemy to draw itself, it needs to know where the player is, to give the right SDL_Rect to the renderer. But it knowing where the player is, is out of its scope. I don't think the enemies need to know where the player is located for drawing purposes. However they will probably know where the player is for other purposes (such as AI), which confuses me even more...

Also, It doesn't seem right for it to be in the renderer object since that brings me back to the kind of code I had before, with getPlayer() type methods.

So, where is the calculation made to offset the SDL_Rect that will be given to the renderer? Where would you put it?

This topic is closed to new replies.

Advertisement