Sign in to follow this  
acid2

C++ - When to use pointers/references?

Recommended Posts

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!

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


Link to post
Share on other sites
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) {}
};

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


Link to post
Share on other sites
The posts about using refs where you have to have valid pointer and pointers for the possiblity of null is a correct answer. You can choose to be as flexible as you want but try to stay with a rule or style to avoid confusion.

Ask the question, is it an abstract collection or concrete instances. I use refs when passing a helper or adaptor class to a new class instance or function. I use Pointers when working with collections or where the object is not always instantiated.

Share this post


Link to post
Share on other sites
Quote:
Original post by 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 ;)


I find the change in code flow annoying.

I find the situations that often demand a goto (multiple nested loops going on at once, with special exceptions to exit) difficult to hold in my head as a single concept.

Finally, I find that gotos lead to fragile code -- because you can easily skip past variable initialization.

So when I see a goto and am trying to understand what is going on, I'm usually in an already multiple-nested procedure, and now I have to figure out if any undefined behaviour slipped through.

Heck, why don't they just throw an exception to exit a loop? Because locally you have no guarantees where either the goto or exception is going to land. Sufficient comments may make up for this, naturally. :)

Can you tell me what the following does without compiling it?

// prints the note on construction and destruction:
struct CtorDtorNote {
const char* note;
CtorDtorNote(const char* note_):note(note_) {
printf("Ctor {%s}\n", note);
}
~CtorDtorNote() {
printf("Dtor {%s}\n", note);
}
};

// play with gotos!
void func() {
CtorDtorNote no_loop = "start of func";
{for (int i = 0; i < 10; ++i) {
CtorDtorNote in_loop = "first note";
{for (int j = 0; j < 10; ++j) {
CtorDtorNote in_loop2 = "second note";
if (j ==2) goto OUT_OF_LOOP;
}}
}}
CtorDtorNote no_loop2 = "strange days";
OUT_OF_LOOP:
return;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by NotAYakk
Finally, I find that gotos lead to fragile code -- because you can easily skip past variable initialization.


Uhm, maybe that's just a special feature of my compiler, but afaik C++ is supposed to be "goto-safe". Goto isn't some disguised asm-op that's free to ignore all rules. Initializing a variable in a part that can be skipped by goto will get you a compiler error. Goto'ing out of some scope WILL free all variables local to this scope. Using goto isn't any less problematic than using new and forgetting delete. But yes, if you have more than the casual goto in rare cases and start peppering your code with them, I wouldn't want to read it.

Quote:
So when I see a goto and am trying to understand what is going on, I'm usually in an already multiple-nested procedure, and now I have to figure out if any undefined behaviour slipped through.


Or... you could simply expect your compiler not to suck and throw an error, if something that actually IS part of C++ (and not some virus that smuggled itself into the standard) is used in an illegal way.

Quote:
Can you tell me what the following does without compiling it?


if (j ==2) goto OUT_OF_LOOP;


Destructors will be called for each object created inside the loops you are breaking out of. If they aren't, throw away your compiler.

CtorDtorNote no_loop2 = "strange days";
OUT_OF_LOOP:


Your compiler will tell you, that the initialization is skipped by the goto, just like it would inside a switch block. Again, if it doesn't, get another compiler.

And if you DO need this no_loop2 for something in the meantime, you can do the same as you would in a switch block. Introduce an artificial scope and do {CtorDtorNote no_loop2 = "strange days";} and everything is fine (except for the brackets looking weird if they are just standing around in the middle of nowhere).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this