Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411520 Posts in 69380 Topics- by 58436 Members - Latest Member: GlitchyPSI

May 01, 2024, 02:56:15 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Pointer or Reference?
Pages: 1 [2]
Print
Author Topic: Pointer or Reference?  (Read 5209 times)
Klaim
Level 10
*****



View Profile WWW
« Reply #20 on: July 14, 2010, 04:36:52 AM »

Vino> As I said, pointers have to be used when necessary (when nullptr is needed, when manipulating raw memory etc). But when programming, you want to avoid errors, undefined behaviour and code that is hard to read.
So when you can use a better solution than pointers to achieve those goals, you should use it instead of pointers.
And there are a lot of solutions, that don't all replace pointers, yes.

That's not hate, that's more like knowing when to use the most useful tool for the task (and respect what is powerful/dangerous).

I would say the same thing about template/metaprogramming because, as pointers, it really really is helpful but you really don't need it everywhere it seem you could use it.

muku++ for reference to "modern usage of C++".

Notice: I use pointers when needed, I don't avoid them as a dogma Wink
Logged

Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #21 on: July 14, 2010, 04:49:42 AM »

PS: There may not be anything about it in the spec, but from the compiler's perspective there's really no way of implementing references without an internal pointer of some sort, that I can think of anyway.

Local references are often implemented using nothing at all.  The compiler simply remembers what the reference refers to and does the swap at compile time, no pointers needed.

For non-local references pointers are generally the preferred implementation, but it could also be conceivably done with a hash table lookup against active variables, in fact I believe this is the preferred technique in C++ interpreters.

No matter how they're implemented, considering references to be pointers is just as silly as assuming the sizeof(int) is 4.  You might get away with it 95% of the time, but it's still implementation defined and could change at any time.
Logged



What would John Carmack do?
B_ill
Level 0
***


View Profile WWW
« Reply #22 on: July 14, 2010, 10:15:15 AM »

This certainly spurred some enlightening debate.
Logged

Game Programmer and Designer
Latest Release: Chemical Cubes for Android and Kindle Fire (iOS coming soon)
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #23 on: July 14, 2010, 12:01:59 PM »

Entirely avoiding pointers is just a cowardly and bad solution.
If you're never going to use them, why even use C/C++?

Not using pointers for any game that takes more than a a night to make is asking for trouble and awkward solutions.
Pointers that are correctly used are powerful, and if you know how to use them, they don't cause trouble.

Who said "entirely avoiding pointers"?

I say "use them when you must, not when you can".

That's no different, because you're never obligated to use them. They're simply very good tools many times.

It's simply a matter of eliminating problem classes.  If I can get away with passing something by reference instead of passing a pointer, I'll do it just about every time.  It completely eliminates the need to check for null pointers.  An entire failure case is eliminated by this choice.  It's smart programming.

Sometimes, I need to allow null as a legal value, or more often I need to store a collection of objects polymorphically.  In those cases, pointers are the way to go, although the later case is coming up less often since I moved to Ada, since Ada supports polymorphic value collections, which rule.

What shocks me is how so many C++ programmers don't take advantage of the language's value semantics.  I imagine it must be people coming from languages like Java and C# where everything is done by pointer.  It pays off to take the time to make your types properly copyable.  In many cases this eliminates the need for pointers or references.

I'm putting the finishing touches on a project in Ada (which has a very similar type system to C++) and by making most of my types copyable, I've managed to use almost no dynamic allocation and very few pointers.  I have no storage issues, I hardly need to worry about deallocations and memory leaks.  It makes life a lot easier.
Logged



What would John Carmack do?
Draknek
Level 6
*


"Alan Hazelden" for short


View Profile WWW
« Reply #24 on: July 14, 2010, 12:51:00 PM »

What shocks me is how so many C++ programmers don't take advantage of the language's value semantics.  I imagine it must be people coming from languages like Java and C# where everything is done by pointer.  It pays off to take the time to make your types properly copyable.  In many cases this eliminates the need for pointers or references.
I almost always use const references rather than passing by value. Do you have some kind of argument why passing by value is better?
Logged

Vino
Level 3
***


View Profile
« Reply #25 on: July 14, 2010, 01:19:16 PM »

I certainly make good use of const references a lot of the time. what gets me is something like,

Code:
void f1()
{
    int* p = &i; // Some pointer just as an example. Say I don't have access to the original object as is often the case.

    f2(p); // Error checking is done by the callee, no problem.

    if (!p) // Error checking is done by the caller, must be done everywhere the function is called.
        f3(*p); // Must dereference the pointer to call f3! This calls the copy constructor, which I might not want! (Doesn't it?)
}

void f2(int* p)
{
    assert(p); // Your favorite error checking method here.

    // do something with p
}

void f3(int& p)
{
    // do something with p
}

so when you're converting a pointer to a reference, you've got to do error checking one way or another. It's much more convenient to create the function with the pointer so that the error checking can be done in one place. The result is that as soon as you start using pointers in one place, they become somewhat viral and start to infect all of the other parts of the code with pointers. If you're going to be dynamically allocating memory, you get a pointer back from "new" and "*alloc" so references tend to go right out the window. If I have a global vector of entities in my game, they can't very well be a vector of objects, or they'd get reallocated every time the vector grows. They have to be a vector of pointers.

But maybe I'm just doing it wrong! I'm willing to learn a better way. In the few cases where I do use references effectively

Code:
void Normalize(const Vector& vecNormalize) { ... }

they're awesome. I just don't see a case for using them everywhere like people seem to be suggesting.
Logged
Vino
Level 3
***


View Profile
« Reply #26 on: July 14, 2010, 01:22:24 PM »

Oh yeah also:

Local references are often implemented using nothing at all.  The compiler simply remembers what the reference refers to and does the swap at compile time, no pointers needed.

For non-local references pointers are generally the preferred implementation, but it could also be conceivably done with a hash table lookup against active variables, in fact I believe this is the preferred technique in C++ interpreters.

Sure but aren't pointers implemented the same way? When you say "the compiler remembers" you mean it stores it in a processor register, which is how pointers are typically done.
Logged
Overkill
Level 3
***


Andrew G. Crowell


View Profile WWW
« Reply #27 on: July 14, 2010, 02:03:09 PM »

Oh yeah also:

Local references are often implemented using nothing at all.  The compiler simply remembers what the reference refers to and does the swap at compile time, no pointers needed.

For non-local references pointers are generally the preferred implementation, but it could also be conceivably done with a hash table lookup against active variables, in fact I believe this is the preferred technique in C++ interpreters.

Sure but aren't pointers implemented the same way? When you say "the compiler remembers" you mean it stores it in a processor register, which is how pointers are typically done.

Well, local references might be optimized away, by just looking in the symbol table at compile-time. In that context, they are name aliases for something that already is defined in scope. So it saves the runtime memory cost of storing a variable's address in a pointer and the runtime cost of indirection, by using the original referenced value directly.

Granted, regular pointers can sometimes be optimized away too, but not as reliably, due to the fact pointers can be reassigned after binding and can be made to point to completely invalid memory addresses.
« Last Edit: July 14, 2010, 02:27:51 PM by Overkill » Logged

muku
Level 10
*****


View Profile
« Reply #28 on: July 14, 2010, 02:10:10 PM »

Code:
        f3(*p); // Must dereference the pointer to call f3! This calls the copy constructor, which I might not want! (Doesn't it?)

No, it doesn't. The copy constructor would only be called if f3 took its parameter by value, but it takes it by reference. What essentially happens in the code you posted is a conversion from one reference type (a pointer) to another one (a reference), which here is a no-op for everything but the type system. So it's perfectly fine to do this (as long as you made sure that p is not null).

Quote
If I have a global vector of entities in my game, they can't very well be a vector of objects, or they'd get reallocated every time the vector grows.

Theoretically they could be, if you implement your copy constructor, assignment operator and destructor properly. (In other words, give them proper value semantics.) However, you lose polymorphism, and it could get quite inefficient if your entities are not small.

Quote
Code:
void Normalize(const Vector& vecNormalize) { ... }

What does that do? It can't change the vector since it's a const reference, and it doesn't return anything.
Logged
Klaim
Level 10
*****



View Profile WWW
« Reply #29 on: July 14, 2010, 02:19:55 PM »

Quote
so when you're converting a pointer to a reference, you've got to do error checking one way or another.

That is only true if you're converting an invalid pointer to a reference and that is bad. The other way around is always right if you don't do this.

So in fact the right way is to put assertions all around pointers usage (getting pointer value, passing it around etc) to make sure it's valid. THEN when you're using references, you don't need to check again, because you assume that your code that have been using pointers was checked.
As you used pointers only where they were necessary, you sudently don't have to check them everywhere in your project, only where necessary (in low level and generic code most of the time).
In other parts of the code, you're passing references or anything else that is suitable, sometimes pointers but only when you need them to be null sometimes. As thay can be null in those parts of the code, your code will naturally take acount of null because it's normal behaviour (like when you return a pointer from a "find" function that could return nothing but life continues if it does).

Then, you start to understand that when you use pointers and references, with and without constness, you, in fact, setup some kind of protocol.
The idea is that when you ask for (non-const) references as function parameter, you suggest that you will modify the given object but it have to exist.
When it's a const reference, you say that you'll just read informations from it but not change it's state in any way (other than mutable state...) but still it have to be an object that exist.
When it's a pointer as parametter, you suggest that, well maybe you'll provide some informations to process, but if you don't (nullptr) it's still fine to execute the function.

When you're returning a const reference from a function, you say that you can read the object but don't touch it!
When you're returning a reference from a function you say that you allow the user to manipulate the object and you suggest (because you write valid code, right?) that the lifetime of the object will be at least as long as the lifetime of the owner of the function (member or not).
If you return a pointer, you suggest that you may, or not, return a result but it's facultative and it's not an error.

So, the practice of reducing use of pointers to where it's necessary will modify how you think and organize your code from bottom layer to top layer, making code easier to manipulate the higher you get.

Quote
Sure but aren't pointers implemented the same way? When you say "the compiler remembers" you mean it stores it in a processor register, which is how pointers are typically done.

Just. Forget. About. Reference. Implementation.

As already said, they can be implemented in memory "like" pointers but they are not adn it's not always the case are nothing in the standard say how they should be implemented.

There is no other guarantee about references than they are just nicknames for objects and that they should then never be uninitialized (if your code is correct).

It's specified that dereferencing a null pointer or a pointer pointing to unallocated memory is undefined behaviour. So you should never get to this point. If you do, then you didn't hear the alarm ringing when you started to cast things around too much.

Back to your example, I would have put checking in the two functions each time I assign a value to a pointer. That's the pointer the source of the problem, not the reference.

Again, there is smart pointers too that help having null pointers without manipulating pointers, but we didn't talk a lot about it here. I  agree with Average Software about the alarming count of C++ programmers that prefer allowing mistakes instead of eliminating them where possible.

Quote
I almost always use const references rather than passing by value. Do you have some kind of argument why passing by value is better?

If you're using native types as parametters it's better to use value than reference (if it's const).
In function returns you'd better return a local object by value (if possible) than by reference as it's life is limited by the scope of the called function. For example you have to return a std::vector by value in a "search" function. But then don't worry much about performance : there is standardized optimizations that occurs most of the time, named NRVO (http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx for example, buyt it's valid for all recent compiler).

(c++ is really a lot of things to keep in mind together to code well... by the way, you should really read this : http://www.tantalon.com/pete/cppopt/main.htm )
Logged

Vino
Level 3
***


View Profile
« Reply #30 on: July 14, 2010, 03:05:55 PM »

Quote
Code:
void Normalize(const Vector& vecNormalize) { ... }

What does that do? It can't change the vector since it's a const reference, and it doesn't return anything.

AHAHA RIGHT guess I didn't think about that one too much, but you know what I mean. Remove the "const" I guess.

Code:
        f3(*p); // Must dereference the pointer to call f3! This calls the copy constructor, which I might not want! (Doesn't it?)

No, it doesn't.

Good to know! This is kind of confusing I think because you're using the dereference operator to create a reference. Does that seem right to you? Somewhere in the back of my head I assumed that the "dereference operator" would always have to make a call to the copy constructor, no matter what the end result type is.

Klaim, maybe I tend to design the core of my games wrong because I always end up using pointers in most places where I don't use handles, which are of course a completely different story. Even the handles though return pointers when they're dereferenced, not references. (Is it even possible to overload the dereference operator to return a reference instead of a pointer?)
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #31 on: July 14, 2010, 03:45:24 PM »

Oh yeah also:

Local references are often implemented using nothing at all.  The compiler simply remembers what the reference refers to and does the swap at compile time, no pointers needed.

For non-local references pointers are generally the preferred implementation, but it could also be conceivably done with a hash table lookup against active variables, in fact I believe this is the preferred technique in C++ interpreters.

Sure but aren't pointers implemented the same way? When you say "the compiler remembers" you mean it stores it in a processor register, which is how pointers are typically done.

I'm talking about something very much like constant propogation, if you know what that is.  Here's an example.

Code:
void funk()
{
    int i;
    int &r = i;

    r = 5;
}

The compiler knows what the referent of r is at compile time.  If can effectively substitute i for r in the assignment.  No storage for the reference is needed at all.

If this were done with a pointer, the code would still have behave as if the pointer had storage, regardless of optimizations performed.  References do not have this requirement.

Quote
Good to know! This is kind of confusing I think because you're using the dereference operator to create a reference. Does that seem right to you? Somewhere in the back of my head I assumed that the "dereference operator" would always have to make a call to the copy constructor, no matter what the end result type is.

The confusion comes from the fact that references and pass/return by reference use the same syntax and name, but they're really two very different things.  You can't pass or return a reference because a reference has no size, there's nothing to pass!  When a reference appears in a parameter list or return type, that is a passing mode, not a reference.  You can't "create" references, because again, they have no size.

Quote
I almost always use const references rather than passing by value. Do you have some kind of argument why passing by value is better?

Passing by const refererence for built-in types is always a losing proposition.  It doesn't give you any performance gain, and causes another problem which I'll get to in a moment.

For user-defined types, I consider passing by const reference to mean, "Here's an object you can do polymorphism with, but you can't change it."  Many people use it a fake pass by value that doesn't copy.  In my opinion, if you mean to pass by value, just pass by value.  If you do some profiling and find that the copy is a problem, sure, pass by const reference.  Pass by value has three advantages:

1.  The function can play with data.

In one of the code bases I used to work with, I'd constantly see people passing const string& to functions, and the first line would then make a local copy of the string for the function to mess around with.  Why not just pass the thing by value in the first place?

2. No aliasing.

The biggest disadvantage to pass by reference is that it introduces aliasing, which is the absolute worst enemy of optimizers.  This is especially true for built-in types.  There's very little a optimizer can do with an aliased value.

3. Virtual functions can be inlined

This can be a big deal sometimes too.  If you have a number of small virtual functions that are inlined, the compiler can't inline them if you pass by reference.  It has no guarantee as to what type the object is.  Passing by value allows the compiler to know the true identity of the object, and all inline functions become available.
« Last Edit: July 14, 2010, 04:37:16 PM by Average Software » Logged



What would John Carmack do?
Klaim
Level 10
*****



View Profile WWW
« Reply #32 on: July 15, 2010, 07:33:13 AM »

Quote
Klaim, maybe I tend to design the core of my games wrong because I always end up using pointers in most places where I don't use handles, which are of course a completely different story. Even the handles though return pointers when they're dereferenced, not references. (Is it even possible to overload the dereference operator to return a reference instead of a pointer?)

It's not "wrong" as you can still achieve a working and efficient game( or whatever software) with or without pointers everywhere. In C it's the only way and it's efficient.

It's just less safe, more error prone. You could avoid a lot of common bugs and concentrate on those specific about your game.
Logged

increpare
Guest
« Reply #33 on: July 15, 2010, 03:00:47 PM »

there's a previous rather detailed discussion of this matter in the latter half of the linked thread.

 Wink
Logged
Pages: 1 [2]
Print
Jump to:  

Theme orange-lt created by panic