Skip to main content
gamedev.net
🔒 Locked

C++ - When to use pointers/references?

Started by acid2 21 replies 4.5k views
Original Post
acid2
acid2
I think the question in the topic mainly sums it up, but I think I also need to ask when should I create objects on the stack and the heap? If I understand correctly, I should use the heap for dynamic objects (short life) and the stack for static objects. I feel quite confident with most aspects of programming, but I think this is the main thing holding me back from experimenting with C++ more. Edit: Thought I should say I do know how to use pointers and references fine (certianly at the beginner level anyway) Any advice appriciated!
Trienco
Trienco
Actually quite simple, but so generic a question that I can't think of anything but examples. Let's take function calls:

func(const HugeObject& x) //good idea
func(HugeObject& x) //good idea, if side effects are _supposed_ to affect x
func(HugeObject x) //not so great, but chances are the compiler will optimize away
func(const int& x) //seriously pointless, int and pointer/reference are the same size
func(const char& x) //bordering on sillyness

More seriously: use pointers and references when they make sense. Prefer references, unless you know you will need to change what it points to later on. At the same time, don't desperately try to use them everywhere. Heap and stack is a question you usually don't have to ask yourself. You would never create an object on the heap with "new" if it's only needed within this one function.

More interesting dilemma would be a situation where you need to return a larger construct, but know it will only be used within the calling function. Construct on the heap and return a pointer (making it the responsibility of the calling function to clean it up again), or for example pass an empty object as reference and just fill it in (maybe with a bool return type to signal an error, where the other method would have returned a null pointer). Of course I wouldn't be surprised, if the compiler starts optimizing any potential difference away, anyway.
f@dzhttp://festini.device-zero.de
acid2
acid2
Cool, thank for those tips, Trienco! I had a feeling it was a "prefer references" case, but that's concreted it as to why (changing memory in the future cannot be done).
Alpha_ProgDes
Alpha_ProgDes
short answer:
references are best used when passing arguments (POD, struct, class, pointer) to a function.

pointers are best used when using data structures (stacks, queues, trees, graphs, linked lists, etc)
Beginner in Game Development?  Read here. And read here.  
acid2
acid2
I assume the reason you want pointers with lists is because you are giving the responsibility to the list. Ie, delete the list - delete the pointers (If it's some type of custom list).
intrest86
intrest86
Here is my opinion since no one else has shared it yet:

Use pointers when a value should be able to have a null value, and references when it shouldn't. So if you are making a function and it CANNOT take a null argument use references. Otherwise use pointers. In the same way, if a function will ALWAYS return a valid object use references, otherwise use pointers so you can return null.

Since the main distinction (I believe) between the two is if they can be null, this system makes sense to me.
Turring Machines are better than C++ any day ^_~
Nitage
Nitage
Quote:
Original post by acid2
I assume the reason you want pointers with lists is because you are giving the responsibility to the list. Ie, delete the list - delete the pointers (If it's some type of custom list).


References always point to the same thing, whereas pointer can be changed to move to other things. This makes it impossible to write containers using references.

As an consequence of this: Prefer to store pointers instead of references in classes/structs - else you won't be able to copy the class.
NotAYakk
NotAYakk
First,

type& variable;
is analagous to
type*const variable;

second, references cannot legally be null.

Third, using reference variables looks like using a "real" variable.

Forth, references behave very differently when stored in a class or struct than pointers do. Never use a reference in a struct or class without knowing what you are doing -- and for spagetti's sake, think what happens when you copy-construct, swap or call operator=.

References are syntactic sugar. Sometimes you want syntactic sugar and sometimes you don't.
Trienco
Trienco
Quote:
Original post by Nitage
As an consequence of this: Prefer to store pointers instead of references in classes/structs - else you won't be able to copy the class.


Why wouldn't you be able to do that? That's why god lets us write our own copy-constructor, so we can make sure all references are initialized in the init-list. Const members and references are behaving pretty much the same. It's not impossible, but you will have to pay a lot of attention when actually want the reference to point to a COPY of the original value in the other object. But generally the whole point of having a reference and not a direct member IS that you don't want each object having its own copy.

Prime example of a really stupid idea (on so many levels):
class Vector {public:  Coordinates& xyz;};


Unless of course, the reference is const and for whatever reason you only have a handful of coordinates, but a gazillion static objects using one of them. In that (somehow extremely unlikely) case it would probably pass as flyweight pattern.

More likely
class Object {  const TextureObject& texture;public:  Object(const TextureObject& t) : texture(t) {}};
f@dzhttp://festini.device-zero.de
Nitage
Nitage
Quote:
Original post by Trienco
Quote:
Original post by Nitage
As an consequence of this: Prefer to store pointers instead of references in classes/structs - else you won't be able to copy the class.


Why wouldn't you be able to do that? That's why god lets us write our own copy-constructor, so we can make sure all references are initialized in the init-list. Const members and references are behaving pretty much the same. It's not impossible, but you will have to pay a lot of attention when actually want the reference to point to a COPY of the original value in the other object. But generally the whole point of having a reference and not a direct member IS that you don't want each object having its own copy.


And how would you implement the assignment operator? To reiterate - always use pointers in classes unless you really really really have a good reason not to.
Trienco
Trienco
Quote:
Original post by Nitage
And how would you implement the assignment operator?


Assignment != copying, hence it's called assignment and not copy operator. If you want to copy, you use the COPY-constructor (and may want to prefer "Object copy(original)" over "Object copy=original" to avoid the confusion of '=' NOT being the assignment operator when initializing).
You could still assign, but obviously it would change the value of the referenced object and not the reference. If that's not good enough you could still try "memcpy(copy, original, sizeof(Object))".
By your argumentation you shouldn't use const members either?
f@dzhttp://festini.device-zero.de
Xai
Xai
Quote:
Original post by Trienco
Quote:
Original post by Nitage
And how would you implement the assignment operator?


Assignment != copying, hence it's called assignment and not copy operator. If you want to copy, you use the COPY-constructor (and may want to prefer "Object copy(original)" over "Object copy=original" to avoid the confusion of '=' NOT being the assignment operator when initializing).
You could still assign, but obviously it would change the value of the referenced object and not the reference. If that's not good enough you could still try "memcpy(copy, original, sizeof(Object))".
By your argumentation you shouldn't use const members either?


I just want to ask Trienco, do you really propose a general purpose programming strategy where copy-construction and assignement has different final results? Are you trying to defend a system in which:

MyClass a(1,3,4,5);
MyClass b(a);

does not have the exact same final result as:

MyClass a(1,3,4,5);
MyClass b;
b = a;

cause if you are, your client code is going to completely impossible to write correctly (it seems to me). Of course you could do this IF AND ONLY IF only one of the 2 operations are supported, but never if both exist should they differ in any way.
NotAYakk
NotAYakk
Quote:
I just want to ask Trienco, do you really propose a general purpose programming strategy where copy-construction and assignement has different final results? Are you trying to defend a system in which:

MyClass a(1,3,4,5);
MyClass b(a);

does not have the exact same final result as:

MyClass a(1,3,4,5);
MyClass b;
b = a;

cause if you are, your client code is going to completely impossible to write correctly (it seems to me). Of course you could do this IF AND ONLY IF only one of the 2 operations are supported, but never if both exist should they differ in any way.


typedef int& MyClass;

int one,two;one=two=0;MyClass a=one;MyClass b=a;a=1;printf("%d %d\n",(int)a, (int)b);

// result: 1 1\n

vs
int one,two;one=two=0;MyClass a=one;MyClass b=two;b=a;a=1;printf("%d %d\n",(int)a, (int)b);

// result: 1 0\n

I could see writing a reference-semantics "auto pointer" class in C++. In that case, copy-construction and assignment are very different operations.

The difference between having const pointers in a class and having references in a class is that references in a class implicitly add partial reference semantics to your class, while const pointers generate errors when you try to call the default assignment operator.

Implicit reference semantics are evil. One could argue that explicit reference semantics are evil.

So don't use references in structs or classes without knowing what the hell you are doing.

[Edited by - NotAYakk on May 23, 2006 3:45:38 PM]
Bregma
Bregma
Quote:
Original post by acid2
I think the question in the topic mainly sums it up, but I think I also need to ask when should I create objects on the stack and the heap? If I understand correctly, I should use the heap for dynamic objects (short life) and the stack for static objects.


When you want to take advantage of scoped lifetime of an object, create that object in automatic storage (on the stack). When you need non-scoped control of the lifetime of an object, you need to create it in dynamic storage (on the heap). Sometimes you also need to create an object in static storage. The lifetime of objects in any of the three C++ strage types may be long or it may be short.

I would advise the use of automatic storage where possible, since it best takes advantage of RAII. You can convert dynamic storage to automatic storage semantics using a smart pointer, which can achieve all of the benefits of references without the disadvantages of dumb (raw, naked) pointers. Smart pointers are available in std::tr1, based on those pioneered by boost.
Stephen M. Webb
Professional Free Software Developer
Xai
Xai
I'm very confused by your post NotAYak ... since what I was saying is that you would not want to ever purposefully define such a system, and then you procede to explain how reference members (and constant pointers to a lesser extent) do just that, and that we should avoid them for this reason (in a way that felt somewhat directed to me).

An Trienco, I was only disagreing with the seeming intent of your most recent post - your first post in this thread I think is spot on.
Trienco
Trienco
Quote:
Original post by Xai
I just want to ask Trienco, do you really propose a general purpose programming strategy where copy-construction and assignement has different final results?


I'm not proposing it, I'm simply not banishing it on principle (much like I don't see how a goto to break out of a nested loop is any worse than tons of if's or "outsourcing" the loop to some inline function).

Mostly because if it makes sense to have such a reference, it probably doesn't make sense to do an assignment (so you can simply sabotage the assignment operator) and IF it does, there are still ways to make it work correctly.

I can see why a brute memcpy might not sit well with some, but we are talking about C/C++, not Java or any strict and safe language. Use your fantasy. You want to get to the actual value of the reference, then there are a gazillion ways to do that.

This should work and as a bonus the compiler might even force you to write the assignment operator yourself, to decide HOW it should be treated.

class A {public:  union {    unsigned refVal;    int& v;  };  ...  A& operator=(A& b) {    refVal=b.refVal;    return *this;  }};


Quote:
cause if you are, your client code is going to completely impossible to write correctly (it seems to me).


If something like this ends up in "public" code and not just a small module with a bunch of "Be careful" warnings, then no, I don't think it's a good idea. Else, any of the possible ways to fix the assignment should be fine.


And keep in mind that having pointers in a class is still just as confusing, because without checking the assignment operators implementation you can never know, if = will deep copy or just copy pointer values (and typically the former is prefered). Both cases can easily lead to unexpected results (and typically you find yourself needing exactly what it doesn't do).
f@dzhttp://festini.device-zero.de
izhbq412
izhbq412
References in classes are usually used for single parent - multiple children relationships where the child's parent can never change. If the parent can indeed change, you should use a pointer.
___The essence of balance is detachment. To embrace a cause, to grow fond or spiteful, is to lose one''s balance after which, no action can be trusted. Our burden is not for the dependent of spirit. - Mayar, Third Keeper
NotAYakk
NotAYakk
Quote:
Original post by Trienco
much like I don't see how a goto to break out of a nested loop is any worse than tons of if's or "outsourcing" the loop to some inline function.


Because the code flow with gotos is very hard to understand to someone who isn't intimately familiar with the code?

Oh -- and does goto deal with object desctruction well? I cannot recall.

Personally, I think C++ needs "named blocks" that can be selectively "break"ed out of. It solves one of the bigest remaining "good" reasons to use goto (breaking out of loops early) in a nice way.

Quote:
Mostly because if it makes sense to have such a reference, it probably doesn't make sense to do an assignment (so you can simply sabotage the assignment operator) and IF it does, there are still ways to make it work correctly.

I can see why a brute memcpy might not sit well with some, but we are talking about C/C++, not Java or any strict and safe language. Use your fantasy. You want to get to the actual value of the reference, then there are a gazillion ways to do that.

This should work and as a bonus the compiler might even force you to write the assignment operator yourself, to decide HOW it should be treated.


There is no guarantee what you are writing will work in a given platform. Accessing the raw memory of a non-POD type results in undefined behaviour.

Quote:
And keep in mind that having pointers in a class is still just as confusing, because without checking the assignment operators implementation you can never know, if = will deep copy or just copy pointer values (and typically the former is prefered). Both cases can easily lead to unexpected results (and typically you find yourself needing exactly what it doesn't do).


Which is why I'm generally in favour of explicitly disabling operator= for any non-trivial class. =-)

Quote:
Original post by Xai
I'm very confused by your post NotAYak ... since what I was saying is that you would not want to ever purposefully define such a system, and then you procede to explain how reference members (and constant pointers to a lesser extent) do just that, and that we should avoid them for this reason (in a way that felt somewhat directed to me).

An Trienco, I was only disagreing with the seeming intent of your most recent post - your first post in this thread I think is spot on.


Sometimes you want "reference semantics" for a variable. Given that it already exists in the C++ language, having other variables use the same semantics under operator= isn't completely off the wall. It should be used with caution, and if you don't have a good reason to do it -- don't do it.

But, like many things, you shouldn't rule it out entirely.

Similarly, you should be aware that there are three language-standard possible effects when operator= is called -- pointer, reference and value-semantics.

Value: Each instance has a copy of the data. This data is changed.
Pointer: Each instance refers to data elsewhere. Which data being referred to changes.
Reference: Each instance refers to data elsewhere. The referred to data is changed.

Mixing the above 3 kinds of operator= semantics in the same class is very dangerous. The mere existence of all three kinds of semantics is dangerous.
izhbq412
izhbq412
Quote:
Because the code flow with gotos is very hard to understand to someone who isn't intimately familiar with the code?
Do you speak from experience or did you read that in a book? Because I don't find using gotos for breaking out of a nested loop difficult to understand at all. Although I do prefer a private (and hopefully) inline function if I'm using C++, not C. Sometimes.

Sorry for the offtopic, but he started it ;)
___The essence of balance is detachment. To embrace a cause, to grow fond or spiteful, is to lose one''s balance after which, no action can be trusted. Our burden is not for the dependent of spirit. - Mayar, Third Keeper

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.