Pass-by-reference instead of pass-by-pointer = a bad idea?

M

Mr A

Hi!
I've been thinking about passing parameteras using references instead
of pointers in order to emphasize that the parameter must be an
object.

Exemple:
void func(Objec& object); //object must be an object

instead of

void func(Object* object); //object can be NULL

I belive that this is a good idea since , in the reference case, it's
clear that NULL is not an option. It's also clear that NULL is an
option when a pointer is expected (stating the obvious :) ). The code
becomes somewhat more self-documenting.

Any comments on why this could be a bad idea or do you think it's just
a matter of taste?

/H
 
A

Alan Johnson

Mr said:
Hi!
I've been thinking about passing parameteras using references instead
of pointers in order to emphasize that the parameter must be an
object.

Exemple:
void func(Objec& object); //object must be an object

instead of

void func(Object* object); //object can be NULL

I belive that this is a good idea since , in the reference case, it's
clear that NULL is not an option. It's also clear that NULL is an
option when a pointer is expected (stating the obvious :) ). The code
becomes somewhat more self-documenting.

Any comments on why this could be a bad idea or do you think it's just
a matter of taste?

/H

That is exactly the right way to do it. I'd go so far as to say always
prefer a reference over a pointer unless you have an explicit need for a
pointer (i.e. it needs to be reseated for some reason and/or NULL has a
useful meaning). The reason being pretty much the same that you stated.
References are (for me at least) easier to work with, and there is no
worry of accidentally trying to access an object through a "NULL reference".

-Alan
 
S

Steven T. Hatton

Mr said:
Hi!
I've been thinking about passing parameteras using references instead
of pointers in order to emphasize that the parameter must be an
object.

Exemple:
void func(Objec& object); //object must be an object

instead of

void func(Object* object); //object can be NULL

I belive that this is a good idea since , in the reference case, it's
clear that NULL is not an option. It's also clear that NULL is an
option when a pointer is expected (stating the obvious :) ). The code
becomes somewhat more self-documenting.

Any comments on why this could be a bad idea or do you think it's just
a matter of taste?

/H

Unfortunately, there are different vantage points from which we view a
function: declaration, definition and invocation. Each has it's own
exposed and hidden information. It has been argued that passing a pointer
is more explicit, and makes the code easier to read. I tend to agree.

AFAIK, you *can* pass a null to func() in your example, and the compiler
will accept it. Your code will segfault when you do so. If you pass a
pointer, you can check for null, before accessing it. I, therefore,
suggest using a pointer instead of a reference.
 
L

Larry I Smith

The above paragraph is correct. Attempting to pass ANYTHING except
a ref to an existing instance of the required type will cause a
compile-time error. Unless there is some specific reason to pass a
pointer, using ref's is usually preferred.

Unfortunately, there are different vantage points from which we view a
function: declaration, definition and invocation. Each has it's own
exposed and hidden information. It has been argued that passing a pointer
is more explicit, and makes the code easier to read. I tend to agree.

AFAIK, you *can* pass a null to func() in your example, and the compiler
will accept it. Your code will segfault when you do so. If you pass a
pointer, you can check for null, before accessing it. I, therefore,
suggest using a pointer instead of a reference.

If a function takes a ref, then NULL can not be passed.
Attempting to pass NULL causes a compile error.
For example:

#include <iostream>

struct Stuff
{
int a;
double b;
};

void func(Stuff& obj)
{
obj.a = 1;
}

int main()
{
Stuff s;

func(s);
func(0); // causes a compile error

return 0;
}

Larry
 
S

Steven T. Hatton

Larry said:
Steven said:
Mr A wrote: [...]
I belive that this is a good idea since , in the reference case, it's
clear that NULL is not an option. It's also clear that NULL is an
option when a pointer is expected (stating the obvious :) ). The code
becomes somewhat more self-documenting.


The above paragraph is correct. Attempting to pass ANYTHING except
a ref to an existing instance of the required type will cause a
compile-time error. Unless there is some specific reason to pass a
pointer, using ref's is usually preferred.

By whom?
If a function takes a ref, then NULL can not be passed.
Attempting to pass NULL causes a compile error.
For example:

#include <iostream>

struct Stuff
{
int a;
double b;
};

void func(Stuff& obj)
{
obj.a = 1;
}

int main()
{
Stuff s;

func(s);
func(0); // causes a compile error

return 0;
}

Larry

In your example, you are not trying to pass null, you are trying to pass a
literal. That results in the attempt to create a temporary object of type
int and assign it to the non-const reference. This, however, will compile:

#include <string>
std::string func(std::string& s) { return s; }
int main() {
std::string* s(0);
func(*s);
}

I know of two companies who have concluded that passing by reference is, in
general, a bad choice. Trolltech, and SuSE.
 
J

John Carson

Steven T. Hatton said:
In your example, you are not trying to pass null, you are trying to
pass a literal.

He is passing NULL which is the same as 0.
That results in the attempt to create a temporary
object of type int and assign it to the non-const reference. This,
however, will compile:

#include <string>
std::string func(std::string& s) { return s; }
int main() {
std::string* s(0);
func(*s);
}

Dereferencing a NULL pointer is undefined behaviour by section 8.3.2/4.
When I run your code, the program crashes.
 
S

Steven T. Hatton

Steven said:
Larry said:
Steven said:
Mr A wrote: [...]
I belive that this is a good idea since , in the reference case, it's
clear that NULL is not an option. It's also clear that NULL is an
option when a pointer is expected (stating the obvious :) ). The code
becomes somewhat more self-documenting.


The above paragraph is correct. Attempting to pass ANYTHING except
a ref to an existing instance of the required type will cause a
compile-time error. Unless there is some specific reason to pass a
pointer, using ref's is usually preferred.

By whom?
If a function takes a ref, then NULL can not be passed.
Attempting to pass NULL causes a compile error.
For example:

#include <iostream>

struct Stuff
{
int a;
double b;
};

void func(Stuff& obj)
{
obj.a = 1;
}

int main()
{
Stuff s;

func(s);
func(0); // causes a compile error

return 0;
}

Larry

In your example, you are not trying to pass null, you are trying to pass a
literal. That results in the attempt to create a temporary object of type
int and assign it to the non-const reference. This, however, will
compile:

#include <string>
std::string func(std::string& s) { return s; }
int main() {
std::string* s(0);
func(*s);
}

I know of two companies who have concluded that passing by reference is,
in
general, a bad choice. Trolltech, and SuSE.


Obvious exceptions are:

std::eek:stream& print(std::eek:stream& out) const {...}

and when passing a const reference:

int foo(const BigObject& bo){}
 
S

Steven T. Hatton

John said:
He is passing NULL which is the same as 0.

'0', when it appears in your code, is a literal. If the reference were
const, the code /would/ compile.

#include <string>
std::string cfunc(const std::string& s) { return s; }
int main() {
cfunc(0);
}

It still has a problem. The above aborts when executed. That's one better
than a segfault, but not typically what I want from a program.
Dereferencing a NULL pointer is undefined behaviour by section 8.3.2/4.
When I run your code, the program crashes.

Yes, it segfaults. That was my point.
 
K

Kristo

Mr said:
Hi!
I've been thinking about passing parameteras using references instead
of pointers in order to emphasize that the parameter must be an
object.

Exemple:
void func(Objec& object); //object must be an object

instead of

void func(Object* object); //object can be NULL

I belive that this is a good idea since , in the reference case, it's
clear that NULL is not an option. It's also clear that NULL is an
option when a pointer is expected (stating the obvious :) ). The code
becomes somewhat more self-documenting.

Any comments on why this could be a bad idea or do you think it's just
a matter of taste?

That looks like a very good idea to me, and IIRC, the FAQ agrees. IMHO,
it's more than a matter of style; it's also a matter of safety. If a
parameter should never be null, then don't give it the chance.

Kristo
 
S

Stuart MacMartin

Why make every little routine test over and over again something that
isn't to ever happen?

The question people ask is, IMO, the wrong one.
People ask: When should I pass by reference?
The right question is "When should I pass by pointer?"

and the answer is "Whenever you want to allow a pointer-to-nothing
(i.e. 0)"
which is in my experience almost never.

I go one step further:
Pass by const reference unless the object may change.
This way when you call some code the intent is crystal clear.

I don't buy the argument that someone might then dereference a null
pointer and thus you always have to protect your code. Your interface
is a contract. At least with references you are making your intent
clear. Pass by pointer and you are saying "You're allowed to pass 0".
Too often if you rely on this in other people's code you'll be
surprised by seg faults because they did not handle being passed 0 --
they really meant it had to point to something.

You rely on other things in the contract: that the kind of object is
the kind you ask for (no static_cast downcasting to the wrong type or
your program will crash), that the relationships between objects is set
up, and so forth. For clarity of code and ease of maintainability I've
found passing by reference (const and non-const) leads to far clearer,
more maintainable and more robust code. Adding null pointer checks all
over the place instead of only in the places where the pointers can be
0 hides programmer intent while adding noise to the program and slowing
runtime. So why do it?

Stuart
 
S

Steven T. Hatton

Kristo said:
That looks like a very good idea to me, and IIRC, the FAQ agrees. IMHO,
it's more than a matter of style; it's also a matter of safety. If a
parameter should never be null, then don't give it the chance.

Kristo

This is one man's opinion:
http://doc.trolltech.com/qq/qq13-apis.html
Pointers or References?

Which is best for out-parameters, pointers or references?

void getHsv(int *h, int *s, int *v) const
void getHsv(int &h, int &s, int &v) const


Most C++ books recommend references whenever possible, according to the
general perception that references are "safer and nicer" than pointers. In
contrast, at Trolltech, we tend to prefer pointers because they make the
user code more readable. Compare:

color.getHsv(&h, &s, &v);
color.getHsv(h, s, v);


Only the first line makes it clear that there's a high probability that h,
s, and v will be modified by the function call.

http://en.wikipedia.org/wiki/Matthias_Ettrich
http://www.kde.org/

When I started working with the KDE, I could download it with a 28.8 modem,
build and install it in a matter of a few minutes on a Pentium. It now
occupies 1.2 G on my harddrive. That may (does) represent some bloat, but
it also represents unquestionable progress. Ettrich is a man who
understands the three principles of good software design. User interface,
user interface, and user interface.
 
I

Ian

Kristo said:
That looks like a very good idea to me, and IIRC, the FAQ agrees. IMHO,
it's more than a matter of style; it's also a matter of safety. If a
parameter should never be null, then don't give it the chance.
And passing by reference does this? I don't think so.

Ian
 
L

Larry I Smith

Steven said:
'0', when it appears in your code, is a literal. If the reference were
const, the code /would/ compile.

In C++ NULL is zero (0).

#include <string>
std::string cfunc(const std::string& s) { return s; }
int main() {
cfunc(0);
}

It still has a problem. The above aborts when executed. That's one better
than a segfault, but not typically what I want from a program.


Yes, it segfaults. That was my point.

#include <iostream>
#include <string>

struct Stuff
{
int a;
double b;
};

void func(const Stuff& obj)
{
obj.a = 1;
}

int main()
{
Stuff s;
Stuff *sp = 0;

func(s);
// the next 3 produce comple time errors
func(0);
func(NULL);
func(sp);
// the next one is a stupid programmer error - deref'ing a NULL
// pointer. it crashes before func() is called - a good reason
// to use ref's exclusively rather than pointers.
func(*sp);

return 0;
}


The pros & cons of refs vs pointers could be argued forever.
Advocates of the different approaches will probably never
agree. So let's not beat it to death anymore.

Regards,
Larry
 
S

Steven T. Hatton

Stuart said:
Why make every little routine test over and over again something that
isn't to ever happen?

Not sure what you mean here. If you can ensure that the error cannot
happen, then there's no point in testing for it. OTOH, if you can't be
sure it won't happen, then you either take your chances for the sake of
performance (and should ask yourself if it really is all that costly to
make the check), or you put in an error check.
The question people ask is, IMO, the wrong one.
People ask: When should I pass by reference?
The right question is "When should I pass by pointer?"

and the answer is "Whenever you want to allow a pointer-to-nothing
(i.e. 0)"
which is in my experience almost never.

That is certainly not a universal assumption. I would never intentionally
pass a null pointer unless I had explicit information telling me it was OK.
I go one step further:
Pass by const reference unless the object may change.
This way when you call some code the intent is crystal clear.

I don't buy the argument that someone might then dereference a null
pointer and thus you always have to protect your code. Your interface
is a contract. At least with references you are making your intent
clear.

Assuming the person maintaining the code actually looks at the function
declaration, rather than just the call. It would appear (much to my
amazement) many C++ programmers believe a programmer should *not* look at
the actual source code where the interface is declared.
Pass by pointer and you are saying "You're allowed to pass 0".
Too often if you rely on this in other people's code you'll be
surprised by seg faults because they did not handle being passed 0 --
they really meant it had to point to something.

It seems to me your understanding of the implication of a pointer parameter
is unfounded. The only time I might assume it's safe to pass a null
pointer is if the parameter has a default null value.
You rely on other things in the contract: that the kind of object is
the kind you ask for (no static_cast downcasting to the wrong type or
your program will crash), that the relationships between objects is set
up, and so forth. For clarity of code and ease of maintainability I've
found passing by reference (const and non-const) leads to far clearer,
more maintainable and more robust code.

I certainly prefer the use of operator[](), operator.() on a reference or an
object over doing the same on a pointer. If you are working with a
conceptual design that uses shared objects, you are almost certainly going
to use some kind of smart pointer. You will have to dereference that
somewhere. This is why I find the proposal to overload operator.() and
operator.*() so attractive. Of course some people believe the new move
semantics will obviate all need for such things. Clearly that is not the
case.

At least with a const reference, I *might* be able to catch an exception.
With a non-const reference, a null pointer is a segfault.
Adding null pointer checks all
over the place instead of only in the places where the pointers can be
0 hides programmer intent while adding noise to the program and slowing
runtime. So why do it?

Because code that does use pointers, and proper error checking won't crash
due to a null pointer, but code that uses references to pass variables
*can* crash because of a null pointer being passed to a reference. So far
as I know, there is no way to protect yourself from that, other than
checking for null before dereferencing.
 
S

Steven T. Hatton

Larry said:
In C++ NULL is zero (0).

Exactly my point. It is NOT a null pointer, it is a literal.
#include <iostream>
#include <string>

struct Stuff
{
int a;
double b;
};

void func(const Stuff& obj)
{
obj.a = 1;
}

int main()
{
Stuff s;
Stuff *sp = 0;

func(s);
// the next 3 produce comple time errors
func(0);
func(NULL);
func(sp);

The error on the last example is because you are passing a pointer to a non
pointer type, and has nothing to do with the value of the type. In the
pervious examples you are passing an integer literal which is likewise
illegal.
 
D

David White

Steven said:
Yes, it segfaults. That was my point.

There's no mention of segfaulting in the standard. The behaviour is
undefined. That means that anything can happen, including the function
executing without any apparent problem.

DW
 
D

David White

Mr said:
Hi!
I've been thinking about passing parameteras using references instead
of pointers in order to emphasize that the parameter must be an
object.

Exemple:
void func(Objec& object); //object must be an object

instead of

void func(Object* object); //object can be NULL

I belive that this is a good idea since , in the reference case, it's
clear that NULL is not an option. It's also clear that NULL is an
option when a pointer is expected (stating the obvious :) ).

Not necessarily. Try passing a null pointer to ::strlen(). Just because a
pointer is passed doesn't necessarily mean that the function will permit it
to be null.
The code
becomes somewhat more self-documenting.

Yes, because you at least don't have to bother finding out if a null pointer
is acceptable.
Any comments on why this could be a bad idea or do you think it's just
a matter of taste?

One thing about the reference case is that you can't tell from the call
whether it is pass by value or pass by reference. In the pointer case it is
at least obvious from the call that an address is being passed, so you are
alerted that the function might change the object.

DW
 
S

Stuart MacMartin

Well I suppose most of this comes down to local convention. I prefer to
work with references, and allow a semantic difference between a
parameter passed by reference rather than passed by pointer.

Your comment:
Because code that does use pointers, and proper error checking won't
crash due to a null pointer, but code that uses references to pass
variables *can* crash because of a null pointer being passed to a
reference. So far as I know, there is no way to protect yourself from
that, other than checking for null before dereferencing.

And that's precisely the point.

I figure you should check for null before dereferencing (or otherwise
using what a pointer points to), and once you've safely dereferenced
continue to use that reference. You feel you should not trust your
caller, so insist on checking the pointer over and over again - and
instead of trusting your callers like I do, you trust the people who
write the routines you call. Since you'd check that the address of a
reference isn't 0, you see no semantic difference between passing a
pointer and a reference.

Just out of curiosity: do you do dynamic_cast of all your variables to
make sure the types are the same as the parameters you specified? This
might sound snide, but I've had people downcast or force a cast
incorrectly, with problems only showing up when the data model changes
significantly or subtly on on rare data. If you can't believe they are
passing a reference to an object when you ask for a reference, how can
you believe that they are passing the correct kind of object? It's
easy to fool the compiler into allowing this. The compiler can help
us, but only to a point. Eventually someone has to understand what a
routine is supposed to do before they call it.

Another thing that puzzles me in all of this: doesn't anyone use a
development environment? How can you not know the parameters and
whether they are passed by value or pointer or reference, const or
non-const?

Stuart
 
B

benben

Well you CAN pass a reference to nothing though:

void f(int& i)
{
i ++;
}

int main()
{
int* i = new int;
int& ref = *i;
delete i;

f(ref); // tell me what does ref referencing to?
}

ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top