Tuba
|
|
« on: November 21, 2014, 10:08:33 AM » |
|
So, I come from a pure C background and most times I've used C++ I handled it more like C with classes. I'm trying to properly use the language now and one thing I've never understood is when to use reference to variables and when to use just pointers ?
I've seen codes that use just one of the two, codes that mix them, there doesn't seem to be a standard for it.
So, what do you recomend? Is there any real difference in using a reference or a pointer or it's just "cosmetic"?
|
|
|
Logged
|
|
|
|
Gtoknu
|
|
« Reply #1 on: November 21, 2014, 10:29:27 AM » |
|
Imagine references as pointers but safer. If you just need to change something passed as reference, then avoid pointers. ERROR_CODE DoSomething(int x, int& output) { if(isValid(x)) { output = doBlablabla(x); return NO_ERROR; } else { return ERROR; } } Now if you need to return an allocated resource, deal with memory directly, and all that stuff, you know pointers are there. MyClass& createResource() { MyClass instance; instance.x = 10; return instance; //it's going to work, but instance will be deleted right here, and this will lead to errors when you try to use the returned value. } MyClass& createResource() { MyClass* instance = new MyClass(); instance->x = 10; return *instance; //Works, and will continue to exist after end of the scope, but how do the caller know that it was allocated and he has to delete it? DOn't do this. //also, to delete this, he would have to do `delete &value;` which is no-good. } MyClass* createResource() { MyClass* instance = new MyClass(); instance->x = 10; return instance; //A little better, works, and the caller may know that he has to delete it. But try to find other way to manage your resources, this is bad. } The Rule Of Thumb is: if you can use a reference, do it. It's safer. When you need to deal with memory directly, or return a newly created resource, then you won't be able to return a reference, do it with pointers. BTW: don't believe me, I'm not a C++ guru at all.
|
|
|
Logged
|
wut
|
|
|
Polly
Level 6
|
|
« Reply #2 on: November 21, 2014, 10:31:05 AM » |
|
Use pointers for members / arguments that can be null, otherwise use references ( which is a code-design decision ). It's mostly cosmetic though, the generated assembly is in most situations identical.
|
|
|
Logged
|
|
|
|
oahda
|
|
« Reply #3 on: November 21, 2014, 11:08:50 AM » |
|
Don't use pointers if you can avoid it is a simple guideline I tend to use. Pointers are fine, but usually unnecessary so there is no need to bring that extra potential of shooting oneself in the foot (or more importantly, if you're writing a library or working on a collaborative project, to help others shoot themselves).
Sometimes you'll have to use pointers but might be able to restrict them to point simply to data elsewhere that can't be defined at declaration but must be filled in later (which is not possible with references which must be defined right away), which may still be stack data rather than new allocations on the heap.
Sometimes you'll use pointers just because a library returns them to you, but in that case you're not always in charge of deleting them yourself, or you might at least do it in an abstracted way using a destroyer function.
Polymorphism works with references just as well as pointers, so that sort of stuff need not be a problem (and you could just take the adress of a normal variable using the & operator otherwise, so that's not a problem at all).
On the rare occasions that you do have to allocate new data on the heap and you're using C++11 you might want to look into smart pointers. But they're a hassle to work with, so even then I tend to stick to just declaring unique pointers and then passing the real object around as a reference rather than messing with the other types of smart pointers.
The only reason I know ATM for myself to use pure pointers and the new and delete operators is to put class instances in unions, which need not be a problem in C++11 which is less strict when it comes to unions, but I've had some trouble with that, so I've just gone about it doing it the old way for that one exception and deleting the data in the destructor of the owning object.
|
|
|
Logged
|
|
|
|
Columbo
|
|
« Reply #4 on: November 21, 2014, 12:18:54 PM » |
|
One distinction is that by creating a function signature that takes a reference rather than a pointer, you are making it very clear to the caller that they're not supposed to pass in NULL.
One disadvantage of references is that you cannot use the 'restrict' keyword on them, and if you're very performance focused that can be a significant problem. If you don't know what the restrict keyword is for, then you probably needn't worry about it.
|
|
|
Logged
|
|
|
|
RandyGaul
|
|
« Reply #5 on: November 21, 2014, 01:32:50 PM » |
|
A lot of people say "references are more safe". That's just not true in practice and doesn't even make sense. There is absolutely no difference between pointers and references other than that references have some additional *semantic* rules.
So look up the rules that references add onto pointers and there you go.
|
|
|
Logged
|
|
|
|
Gtoknu
|
|
« Reply #6 on: November 21, 2014, 01:36:46 PM » |
|
On the rare occasions that you do have to allocate new data on the heap and you're using C++11 you might want to look into smart pointers. But they're a hassle to work with, so even then I tend to stick to just declaring unique pointers and then passing the real object around as a reference rather than messing with the other types of smart pointers.
I've been told that smart pointers aren't that good for gamedev. Our memory cycle is a little different from most applications (that's why gamedevs don't get along with GC'd languages), and this kinda make shared pointers and unique pointers not very useful for us. Though, I know they have their place in some gamedev code.
|
|
|
Logged
|
wut
|
|
|
Average Software
|
|
« Reply #7 on: November 21, 2014, 04:06:51 PM » |
|
One disadvantage of references is that you cannot use the 'restrict' keyword on them, and if you're very performance focused that can be a significant problem. If you don't know what the restrict keyword is for, then you probably needn't worry about it.
restrict is a C keyword, not C++, unless C++14 adopted it. As for pointers/references, my general rule is to that pointers denote ownership, references do not. That is, if I give a function something by pointer, that function now owns that thing and is responsible for managing it. It I pass something by reference, it belongs to the caller and the function is just using it. Of course, I prefer pass/return by value over both of these whenever I can get away with it.
|
|
|
Logged
|
What would John Carmack do?
|
|
|
1kW
|
|
« Reply #8 on: November 21, 2014, 05:34:51 PM » |
|
I still don't assimilate why we should use pointers or references. Bring some more light here, please
|
|
|
Logged
|
|
|
|
RandyGaul
|
|
« Reply #9 on: November 22, 2014, 12:59:48 AM » |
|
I still don't assimilate why we should use pointers or references. Bring some more light here, please Are you asking why either should be used at all? They are used to store addresses to locations in memory. If you meant whether to use one over the other, well this gets into opinion land. The address that a pointer holds can be modified during run-time, a reference cannot. References have some additional rules. The real reason why references were originally created is because they use the '.' syntax instead of the '->' syntax. Arrow syntax is the most worthless part of C++, so references can be quite nice in this way.
|
|
|
Logged
|
|
|
|
Boreal
Level 6
Reinventing the wheel
|
|
« Reply #10 on: November 22, 2014, 02:23:20 AM » |
|
References work very well with the concept of RAII. Since a reference cannot be "empty", you are forced to declare it after (or deeper) than the object it is referring to. This makes it much harder to accidentally use an object after it has been destroyed. But they are not a silver bullet when it comes to memory safety - you still have to be wary when referring to anything allocated on the heap, especially if that object is not owned by your code. For example, a reference to an element of a vector may be invalidated as soon as you call push_back(), and so you should not keep these references around. This is why Rust is such a safe language - ownership is codified, and if you don't own the rights to a piece of memory your ability to use (and potentially abuse) it is very restricted. Of course, pointer hacking is sometimes a necessary evil, and perfectly safe if you know what is going on with the memory. Here's a particularly nasty snippet I wrote that involves (among other things) a horrible, horrible cast from an immutable pointer to a mutable generic one and back again. But I know the usage patterns and lifetime of my memory to a T, and so this seemingly unsafe code is quite safe. // redefine print to use the log lua_pushlightuserdata(g_vm, (void *)&log); lua_pushcclosure(g_vm, [](lua_State *vm)->int { // retrieve the captured log pointer const Log *log = (const Log *)lua_topointer(vm, lua_upvalueindex(1));
// print all arguments int n = lua_gettop(vm); for(int i = 1; i <= n; ++i) { log->writeNormal(lua_tostring(vm, i), false); if(i < n) { log->writeNormal("\t", false); } } log->writeNewline();
return 0; }, 1); lua_setglobal(g_vm, "print");
|
|
« Last Edit: November 22, 2014, 02:32:19 AM by Boreal »
|
Logged
|
|
|
|
Columbo
|
|
« Reply #11 on: November 22, 2014, 04:05:18 AM » |
|
One disadvantage of references is that you cannot use the 'restrict' keyword on them, and if you're very performance focused that can be a significant problem. If you don't know what the restrict keyword is for, then you probably needn't worry about it.
restrict is a C keyword, not C++, unless C++14 adopted it. Thanks, didn't realise that restrict wasn't standardised in C++. I think most compilers do have an equivalent though, including many consoles and up-to-date versions of Visual Studio, Clang and GCC.
|
|
|
Logged
|
|
|
|
Krux
|
|
« Reply #12 on: November 22, 2014, 10:42:59 AM » |
|
On this topic everybody has it's own guidelines. Mine is, don't use raw pointer at all, if you can avoid it. Use references where it is possible. If you need pointers, use the smart ones (eg. std::unique_ptr). And don't use any operators like + ++ - -- [] on pointers. if you want to iterate, use range based for, if you need to access an array, use std::vector instead. Sometimes there is no way around pointers, but not very often.
|
|
|
Logged
|
|
|
|
oahda
|
|
« Reply #13 on: November 22, 2014, 11:38:39 AM » |
|
A lot of people say "references are more safe". That's just not true in practice and doesn't even make sense. There is absolutely no difference between pointers and references other than that references have some additional *semantic* rules.
So look up the rules that references add onto pointers and there you go.
I agree. Don't get me wrong. I suppose the memory leaks that people are so afraid of are less of a problem on modern systems, too.
|
|
|
Logged
|
|
|
|
Tuba
|
|
« Reply #14 on: November 22, 2014, 12:32:53 PM » |
|
I guess pointers are safe as long as you know well what you are doing.
Just remember to free everything you allocate and you should be fine about memory leaks, I usually try to keep my code more or less "symmetrical". Problem really starts when you need to have a pointer to a pointer... and than a pointer to that pointer, and so on...
|
|
|
Logged
|
|
|
|
oahda
|
|
« Reply #15 on: November 23, 2014, 02:13:58 AM » |
|
anyway #1 reason to avoid pointers is the arrow syntax is ugly
|
|
|
Logged
|
|
|
|
anthnich
|
|
« Reply #16 on: November 25, 2014, 08:39:46 AM » |
|
Pointers are best used to maximize polymorphism and memory allocations & management. Pointers also make handling "outside of scope" variable/memory usage much easier.
The biggest caveat of pointers is that you have to understand and keep track what you are pointing to/allocating. A good way to help take the burden off is to use smart pointers.
|
|
|
Logged
|
|
|
|
oahda
|
|
« Reply #17 on: November 26, 2014, 12:27:49 AM » |
|
My current project uses a smart unique pointer for a main game object so that it's killed once the program ends – the rest is stored inside that object (it's a component-based system) and so everything else goes out of scope or is deallocated recursively in its destructor. There aren't many allocations or deallocations to think of at all in my system, and it's quite nice.
I mostly use pointers for the SDL struct objects since SDL is a C library, and for some other data returned from pointer-loving libraries that I use. But it's all hidden away in the framework part of the project, so the game part of it really doesn't need to use pointers at all. Component creation is centralised and abstracted.
I sometimes use pointers or references to grab direct handles to subcomponents in the parent's constructor and put them into member variables so that I don't have to access them through slower function calls in real time, but that's obviously not a matter of de/allocations to think of.
|
|
|
Logged
|
|
|
|
Rusk
Level 1
|
|
« Reply #18 on: November 28, 2014, 01:05:45 PM » |
|
Herb Sutter gave a talk recently about style guidelines for c++. There was a section with suggestions for when to use pointers and refs in function calls and noob as I am I thought it was pretty good. Start at 51 minutes for the parameter passing stuff, or skip to 55:20 for a close view of the actual slide:
|
|
|
Logged
|
|
|
|
|