Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

& -- reference operator

Status
Not open for further replies.

zildjohn01

Programmer
Sep 6, 2005
10
US
can someone help me understand the & reference operator, such as:

Code:
void MyFunc(int& val) {}

the way i understand this code is, when you access the code it is as it has been declared:

Code:
int x;

but contains a hidden pointer where the data actually is. so:

Code:
void f1(int& data) {data = 7;}
void f2(int data) {data = 7;}
void f3(int *data) {*data = 7;}
void main() {
	int x = 5;
	f1(x);
	// now x == 7 ???

	x = 5;
	f2(x);
	// now x == 5;

	x = 5;
	f3(&x);
	// now x == 7;
}

is that correct? where should you use & instead of * for reference parameters? is there automatic destruction? documentation/examples on this seemed sparse...

thx in advance :)
 
Yup; that's right.

You'll have to decide for yourself when to use & instead of *.

A lot of C programmers believe that C++ references are evil because code calling functions with reference parameters provides no indication that the function may change the parameter.

For example, in your above code, a programmer reading
Code:
x = 5;
f3(&x);
immediately knows that [tt]f3[/tt] may have modified [tt]x[/tt].

However, that fact is not so clear when looking at
Code:
x = 5;
f1(x);


From a higher level perspective, however, one might argue that a programmer shouldn't have to look up or memorize whether he needs to pass a variable by value or by reference before calling a function. From that point of view, C++ references are a valuable tool, and should always be used for passing parameters by reference.


If you write code for a company, it may have a style guide for you to follow. If not, you'll have to make the decision on your own. That'll depend on your views and the views of those likely to read your code.


However, there's one case that I'd argue references should always be used: when calling a function with a parameter that will never be modified, but is passed by reference simply to avoid making an unnecessary copy of it, you should always pass a C++ reference to const.

That's because, since you aren't modifying the parameter, you shouldn't go out of your way to make it clear to readers that it's being passed by reference.
 
It also depends where you've come from and where you intend to go to. If you have come from a Java/C# background or are intending to go to a Java/C# environment where everything is done by reference and pointers are non existent, then it would be useful to get used to the idea of references.

If you've come from a C environment, just treat references as nothing more than a pointer without the dereferencing. The main reason why C++ has references is because they are used in operators.

Also be aware that it is possible to have a pointer reference. I only ever use this when most of the programmers come from a Java background (especially fresh graddies) - they seem to have a mental block on the use of multiple asterisks.
Code:
void ptrref (int*& ptrnew)
{
   ptrnew = &someglobalarray;
}

Alternatively
void ptrptr (int** ptrnew)
{
   *ptrnew = &someglobalarray;
}
 
Let's remember a program context where C++ references are essentials: user-defined class operators. Try to (re)define your class operator <<() or operator =() without references...
 
Only use pointers if you actually need to.

A pointer may be NULL where in C++ there is no such thing as a NULL-reference (though you technically can accomplish one - but then you're far out in the blue...)

Variants - consider using them in this order
If f1 aint enought consider f2.
If f2 aint enough consider f3.
Consider f4 only if you absolutely must.

Code:
// Regular in param, f1 can't modify it
void f1(const SomeType& param);

// in/out param, f2 can modify it
void f2(SomeType& param);

// In as const pointer, f3 can't modify it 
// but must consider a potential param==NULL situation
void f3(const SomeType* param);

// Most hazardus signature of all
// f4 must consider param==NULL, f4 can modify
// param and even delete it!
void f4(SomeType* param);

/Per
[sub]
www.perfnurt.se[/sub]
 
> A pointer may be NULL where in C++ there is no such thing
> as a NULL-reference

Quite simply - how do you pass reference to dynamically created objects - through *obj_ptr, and obj_ptr can easy be NULL. So reference/pointer parameters differ only in syntax point of view, internally and functionally they are the same.
 
mingis said:
Quite simply - how do you pass reference to dynamically created objects - through *obj_ptr, and obj_ptr can easy be NULL. So reference/pointer parameters differ only in syntax point of view, internally and functionally they are the same.
I disagree.

If you have a function with a reference parameter, then your function can assume that the reference is valid. If somebody has an obj_ptr that is null and passes it as *obj_ptr, then they have invoked undefined behavior by dereferencing a null pointer even before your function is called.

So really the only way you could have a "null reference" is if undefined behavior was already reached. That is a very important difference from pointers.
 
I agree that internally they are the same. But functionally? No!

>obj_ptr can easy be NULL

As I said earlier: though you technically can accomplish one - but then you're far out in the blue






/Per
[sub]
www.perfnurt.se[/sub]
 
Agree. The main difference that NULL-pointer could be a fully acceptable value, usually meaning parameter absence, where reference parameter is meant allways present. But this should not be reason to pay less attention for checking of legality of parameters passed to the function. Initialising all possible pointers to NULL and checking all reference parameters not to be not NULL in initial parts of functions is approach to avoid crashes due to referencing to unallocated memory.
 
>But this should not be reason to pay less attention for checking of legality of parameters passed to the function

Can't argue with that. That's a good point. I assume you mean checking by ASSERT or similar bug-trapping technique and not acting as if null-reference is a valid situation used for some "if(&ref == NULL)" crap in the code.



/Per
[sub]
www.perfnurt.se[/sub]
 
Except for possibly in extremely sensitive applications, I can't see any reason why you would ever check a reference to make sure it is not a derefernced null pointer. It is a waste of CPU cycles, or at least a waste of code space if you use asserts. Why spend energy handling situations where the caller has already broken the program?

If the calling code is dereferencing a pointer to pass it to a function taking a reference, then it is the calling code's responsibility to check for null before the dereference. Again, once the dereference of the null pointer takes place, the program is already broken even before your function is called.
 
>Why spend energy handling situations where the caller has already broken the program?


To detect it? Just because its the caller's fault doesn't mean its bad do check it, just as you'd use a REQUIRE contract on the input params when you design by contract.

Other than that, I agree, and I personally never check references addresses, mainly beacuse I never deref. null....

/Per
[sub]
www.perfnurt.se[/sub]
 
> I can't see any reason why you would ever check a
> reference to make sure it is not a derefernced null
> pointer.

For debuging purposes - I don't know easy way to find buggy place when the program produces segmentation fault message only. There could be quite common situations, when usage of not allocated objects could appear. For example, when objects are created in handler of WM_CREATE message and accessed somewhere else, where is probably unknown, whether WM_CREATE is allready arrived or not yet.

> It is a waste of CPU cycles, or at least a waste of code
> space

I think, ASSERT's are excluded from the release code. If not, it is possible to envelope testing code lines into something like #ifdef DEBUG as well (there should be in VC++ some predefined constant like that).

> the calling
> code's responsibility to check for null before the
> dereference.

For example, there are 1000 calls to the function - so 1000 testings? It is more simply to test it in one place - inside of the function.

> Again, once the dereference of the null pointer takes
> place, the program is already broken even before your
> function is called.

As function call procedures internally are identical in both - pointer and reference - cases, program will crash, only when the null-pointer will be referenced - inside of the called function. You are right only in case, when you access object members directly. Class methods are called without referencing to class objects, "this" is passed to class method functions as an additional hidden usual pointer parameter. For example, following code works:

Code:
#include <iostream>

class MyClass
{
public:   
   void TestPresence(void);
};

void MyClass::TestPresence(void)
{
   if(this!=NULL) cout << "OK\n";
   else cout << "Object not yet created\n";
}

MyClass *my_obj=NULL;
   
int main(void)
{
   my_obj->TestPresence();

return(0);
}
 
>I think, ASSERT's are excluded from the release code.

They are by default.

>You are right only in case, when you access object members directly

Or when you call virtual methods.

>For example, following code works

I'd publicly spank who ever would do method calls on null-classes in my code base....

/Per
[sub]
www.perfnurt.se[/sub]
 
mingis said:
For example, there are 1000 calls to the function - so 1000 testings? It is more simply to test it in one place - inside of the function.
If there are 1000 pointer dereferences in a place where the pointer might be null, then absolutely there should be 1000 testings.

Besides, if you take a reference parameter, a lot of the time the argument will be a stack-based variable or a reference itself, so the check for null is a waste. If you move the null check to the point of the dereference, then you are using it only were it might be needed. That is the more simple solution in my opinion.

I can see adding a check for a "null reference" if a particular piece of code is giving you trouble, but to add it into your function from the start seems gratuitous.

Your example seems to assume bad coding. I might not spank anybody, but I would hope that my fellow coders would write proper code. Times when hacks like that are required should be rare and can be handled at that time.
 
Code:
void MyClass::TestPresence(void)
{
   if(this!=NULL) cout << "OK\n";
   else cout << "Object not yet created\n";
}
Superb!
It's exactly:
Code:
If I'm not in existence or dead then I would say...
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top