By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,679 Members | 1,063 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,679 IT Pros & Developers. It's quick & easy.

Pointers vs References: A Question on Style

P: n/a
I've read articles like Scott Meyer's EC++ (Item 22) that advocate the use
of references when passing parameters. I understand the reasoning behind
using references--you avoid the cost of creating a local object when you
pass an object by reference.

But why use a reference? Is there any inherent advantage of using a
reference over using a pointer? My workplace discourages the use of
pointers when it comes to parameter passing. They always prefer references.

I would argue one reason why we might consider using a pointer is to help
communicate intent. For example, suppose I do the following in C:

#include "foo.h"

int main(int argc, char *argv[])
{
int x=5;

foo(x);

printf("%d\n",x);

return 0;
}

I can guarantee that the program will print '5' because C only supports
pass-by-value. If we're using C++, I can't make that guarantee because
we're allowed to pass-by-reference.

But if I use pointers in parameter-passing, I can use the address-of
operator to communicate intent to other programmers that the parameter
is meant to be modified. For example,

#include "A.h"
#include "X.h"

int main(int argc, char *argv[])
{
A a;
X x;

x.foo(&a); // the intent of this member function is to modify 'a'
// A non-const member function of 'a' will be called.

x.bar(a); // this member function will NOT modify 'a'. Only const
// member functions of 'a' will be called.

std::cout << a << std::endl;

return 0;
}

If I'm having problems with object 'a', I see that member function bar()
will not change object 'a' (by convention), and that my problem is likely
in member function foo(). This is especially useful if I'm sifting through
a large amount of code to track down a problem. By using this convention, I
can save time not having to look at every member function in the class
declaration to see which ones might be the cause of my problem.

Of course, all programmers have to adhere to the convention in order for it
to be useful. So to make a long story short, is it better to do
this:

void x::foo(A* a); // foo() will modify 'a'
void x::bar(const A& a); // bar() will not modify 'a'

Or is it better to do what my workplace requires?

void x::foo(A& a); // foo() will modify 'a'
void x::bar(const A& a); // bar() will not modify 'a'

Am I wrong in suggesting the use of pointers for parameter-passing? Is
there a reason why a reference should always be preferred over a pointer?
My thinking is that this convention would make the C++ code more readable.

----
Desmond

(Remove the 'nospam' from the address for e-mail replies, but I prefer
reply posts to the newsgroup)
Jul 22 '05 #1
Share this Question
Share on Google+
26 Replies


P: n/a
There are 2 and only 2 situations in which I choose a pointer over a
reference:

1) When it must be re-seated

2) When arrays are involved

I understand the reasoning behind
using references--you avoid the cost of
creating a local object when you
pass an object by reference.

Incorrect. The nature of today's computers (ie. stack and registers) still
require a hidden pointer. (This is ofcourse where outline functions are
involved).
-JKop
Jul 22 '05 #2

P: n/a
>There are 2 and only 2 situations in which I choose a pointer over a
reference:

1) When it must be re-seated

2) When arrays are involved


Another possibility is when a function argument is optional. Pointers can be
null whereas references cannot.
Jul 22 '05 #3

P: n/a
DaKoadMunky wrote:
There are 2 and only 2 situations in which I choose a pointer over a
reference:

1) When it must be re-seated

2) When arrays are involved
Another possibility is when a function argument is optional. Pointers can

be null whereas references cannot.
One good style rule is to never return null, and never accept null as
parameter.

Follow that rule by passing in instead a Null Object. Consider this code:

void funk(SimCity const * pCity)
{
if (pCity)
pCity->throwParade();
}

Now contrast with this:

class NullCity: public SimCity
{
public: /*virtual*/ void throwParade() {}
};

void funk(SimCity const & aCity)
{
aCity.throwParade();
}

The code simplifies because it pushes behavior behind an interface. The
interface simply promises to its caller that it is doing something, whether
or not it really is. Patterns like this are the heart of OO - programming to
the interface instead of the implementation.

Desmond Liu wrote:
But why use a reference? Is there any inherent advantage of using a
reference over using a pointer? My workplace discourages the use of
pointers when it comes to parameter passing. They always prefer

references.

You need to pair-program with your colleagues, because if they know just
enough C++ to write that advice down, they probably know much more verbally.

The C++ keyword const instructs compilers to reject overt attempts to change
a variable's value. Covert attempts produce undefined behavior, meaning
anything could happen.

C++ functions can take arguments by copy, by address, or by reference.
Ideally, if an object passed into a function does not change, the object
should pass by copy:

void foo(SimCity aCity);

That code is inefficient. In general, programmers should not stress about
efficiency until they have enough code to measure it and find the slow
spots. In this situation, a more efficient implementation is equal cost.
When we pass by reference, our program spends no time making a huge copy of
an entire city:

void foo(SimCity &aCity);

Now if foo() won't change that city's value, the function should declare
that intention in its interface, using pass-by-constant-reference to
simulate pass-by-copy:

void foo(SimCity const &aCity);

That is the most efficient call syntax, cognitively and physically. It's
cognitively efficient because it gives foo() no copied object to foolishly
change and then discard. Statements inside foo() that might attempt to
change that city shouldn't compile. It's physically efficient because the
compiler produces opcodes that only give foo() a handle to an existing city,
without copying it.

C++ supports qualifications before their qualified types, such as "const
SimCity &". I try to write expressions with the most important part first.
There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces

Jul 22 '05 #4

P: n/a
I prefer to use references over pointers when I can. One of my strong
reasons is a reference should be referencing a valid object, whereas
a pointer you should always check that it is not NULL before using
it. The code is a little bit less "noisy" since I don't have the -> or *
dererencing to look at.

In situations where an object may be passed or not, or may be present
or not, a pointer is needed.

dave

"Desmond Liu" <dl************@shaw.ca> wrote in message
news:Xn*********************************@64.59.144 .76...
I've read articles like Scott Meyer's EC++ (Item 22) that advocate the use
of references when passing parameters. I understand the reasoning behind
using references--you avoid the cost of creating a local object when you
pass an object by reference.

But why use a reference? Is there any inherent advantage of using a
reference over using a pointer? My workplace discourages the use of
pointers when it comes to parameter passing. They always prefer references.
I would argue one reason why we might consider using a pointer is to help
communicate intent. For example, suppose I do the following in C:

#include "foo.h"

int main(int argc, char *argv[])
{
int x=5;

foo(x);

printf("%d\n",x);

return 0;
}

I can guarantee that the program will print '5' because C only supports
pass-by-value. If we're using C++, I can't make that guarantee because
we're allowed to pass-by-reference.

But if I use pointers in parameter-passing, I can use the address-of
operator to communicate intent to other programmers that the parameter
is meant to be modified. For example,

#include "A.h"
#include "X.h"

int main(int argc, char *argv[])
{
A a;
X x;

x.foo(&a); // the intent of this member function is to modify 'a'
// A non-const member function of 'a' will be called.

x.bar(a); // this member function will NOT modify 'a'. Only const
// member functions of 'a' will be called.

std::cout << a << std::endl;

return 0;
}

If I'm having problems with object 'a', I see that member function bar()
will not change object 'a' (by convention), and that my problem is likely
in member function foo(). This is especially useful if I'm sifting through
a large amount of code to track down a problem. By using this convention, I can save time not having to look at every member function in the class
declaration to see which ones might be the cause of my problem.

Of course, all programmers have to adhere to the convention in order for it to be useful. So to make a long story short, is it better to do
this:

void x::foo(A* a); // foo() will modify 'a'
void x::bar(const A& a); // bar() will not modify 'a'

Or is it better to do what my workplace requires?

void x::foo(A& a); // foo() will modify 'a'
void x::bar(const A& a); // bar() will not modify 'a'

Am I wrong in suggesting the use of pointers for parameter-passing? Is
there a reason why a reference should always be preferred over a pointer?
My thinking is that this convention would make the C++ code more readable.

----
Desmond

(Remove the 'nospam' from the address for e-mail replies, but I prefer
reply posts to the newsgroup)

Jul 22 '05 #5

P: n/a
Dave Townsend wrote:
In situations where an object may be passed or not, or may be present
or not, a pointer is needed.


Read my post.

I think I might be able to extend the NullObject concept to say not using it
violates the Liskov Substitution Principle...

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces
Jul 22 '05 #6

P: n/a
Agreed, but using a NullObject only relates to new classes, not
legacy ones I have to live with.

"Phlip" <ph*******@yahoo.com> wrote in message
news:pd****************@newssvr31.news.prodigy.com ...
Dave Townsend wrote:
In situations where an object may be passed or not, or may be present
or not, a pointer is needed.
Read my post.

I think I might be able to extend the NullObject concept to say not using

it violates the Liskov Substitution Principle...

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces

Jul 22 '05 #7

P: n/a
Dave Townsend wrote:
Agreed, but using a NullObject only relates to new classes, not
legacy ones I have to live with.


Read /Working Effectively with Legacy Code/ by Mike Feathers.

Then think globally and act locally. One little NullObject at one interface
won't bring down the whole house of cards.

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces
Jul 22 '05 #8

P: n/a
JKop <NU**@NULL.NULL> wrote in news:zO*****************@news.indigo.ie:
There are 2 and only 2 situations in which I choose a pointer over a
reference:

1) When it must be re-seated

2) When arrays are involved

I understand the reasoning behind
using references--you avoid the cost of
creating a local object when you
pass an object by reference.

Incorrect. The nature of today's computers (ie. stack and registers)
still require a hidden pointer. (This is ofcourse where outline
functions are involved).
-JKop


Whoops. That was a typo. That should have read "avoid the cost of creating
a local object when you pass an object by _value_, as per Scott Meyer's
advice. Sorry.

Desmond
Jul 22 '05 #9

P: n/a

"Phlip" <ph*******@yahoo.com> wrote in message
news:MC**************@newssvr33.news.prodigy.com.. .
C++ supports qualifications before their qualified types, such as "const
SimCity &". I try to write expressions with the most important part first.
There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.


Hi Phlip, can you elaborate on this? I've been using "const datatype &data"
rather than "datatype const &data". What are the differences between these
two semantic styles?


Jul 22 '05 #10

P: n/a
>One good style rule is to never return null, and never accept null as
parameter.

Follow that rule by passing in instead a Null Object. Consider this code:

void funk(SimCity const * pCity)
{
if (pCity)
pCity->throwParade();
}

Now contrast with this:

class NullCity: public SimCity
{
public: /*virtual*/ void throwParade() {}
};

void funk(SimCity const & aCity)
{
aCity.throwParade();
}


Two questions...

What if calling SimCity::throwParade requires the caller to ensure certain
preconditions are met? Suppose ensuring those preconditions is expensive. It
seems that using a NullCity here and not checking for it (or a 0 value) could
result in unnecessary code being executed.

What if the caller of SimCity::throwParade writes code that is dependent upon
the postconditions associated with SimCity::throwParade? I am not sure how a
NullCity can be reasonably substituted in this case. Because it is a "do
nothing" object it doesn't seem correct to have it guarantee postconditions nor
dor does it seem correct for calling code to respond as though an error was
present because postconditions were not met.

I am in the midst of reading about NullObject at
http://c2.com/cgi/wiki?NullObject. Maybe I will find the answer there. I am
curious as to your response though.

Thanks.


Jul 22 '05 #11

P: n/a
Joe C wrote:

Phlip wrote:
C++ supports qualifications before their qualified types, such as "const
SimCity &". I try to write expressions with the most important part first. There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.


Hi Phlip, can you elaborate on this? I've been using "const datatype

&data" rather than "datatype const &data". What are the differences between these two semantic styles?


It's only style - the base type should go first, so you read it first when
you scan a line.

The only technical reason I can think of is this:

#define datatype someType*
//typedef someType * datatype;

Only the post-fixed 'const' works the same for each of those two different
ways to declare datatype.

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces
Jul 22 '05 #12

P: n/a
DaKoadMunky wrote:
void funk(SimCity const & aCity)
{
aCity.throwParade();
}
Two questions...

What if calling SimCity::throwParade requires the caller to ensure certain
preconditions are met? Suppose ensuring those preconditions is expensive.

It seems that using a NullCity here and not checking for it (or a 0 value) could result in unnecessary code being executed.
Uh, move those expensive things into delegates of SimCity. Call them
meetPreconditions(). Then give NullCity an empty implementation of that
method.

H. S. Lahman wrote this:

Responding to Phlip...
The Liskov Substitution Principle states (roughly) that users of polymorphic types must not be required to detect which derived type responds to an
interface.

A thread on another newsgroup lead me to suspect this violates LSP:

void funk(SimCity * pCity)
{
if (pCity != NULL)
pCity->throwParade();
}

By that estimation, NULL pointers in interfacial C++ are history.
NullObject - or better - is the way to go.
I agree with Wissler. Checking a NULL pointer is merely a check on
relationship conditionality, which is quite valid.

OTOH, one has to wonder why a NULL pointer is being passed to funk. If
anything funny is going on, the damage was already done in the caller,
such as:

Client::doIt (City* pCity)
{
SimCity* pSimCity; // subclass of City

pSimCity = dynamic_cast<SimCity>(pCity);
funk (pSimCity);
}
FWIW, I think this is just another example of why it is a bad idea to
pass object references except as a setter for a referential attribute.
Conditionality in relationships presents enough problems to the client
without combining it with temporary relationship instantiation.

*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

----8<-----------------------------------
What if the caller of SimCity::throwParade writes code that is dependent
upon
the postconditions associated with SimCity::throwParade? I am not sure
how a
NullCity can be reasonably substituted in this case. Because it is a "do
nothing" object it doesn't seem correct to have it guarantee
postconditions nor
dor does it seem correct for calling code to respond as though an error
was
present because postconditions were not met.


You may want to post this to my thread on news:comp.object!

I know I would continue to test for null-ness where needed, and would
attempt to push those sensitive things into a delegate that naturally goes
away when the NullObject is around. But like the "pimpl idiom", NullObject
is the target of an emergency refactor, not a design goal by itself. I think
your sensitive things already violated LSP before replacing the pointer with
the NullObject.

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces


Jul 22 '05 #13

P: n/a
"Phlip" <ph*******@yahoo.com> wrote in message news:<MC**************@newssvr33.news.prodigy.com> ...
DaKoadMunky wrote:
There are 2 and only 2 situations in which I choose a pointer over a
reference:

1) When it must be re-seated

2) When arrays are involved
Another possibility is when a function argument is optional. Pointers can

be
null whereas references cannot.


Typically, you should pass parameters *by* *address*

1. whenever the parameter is going to be modified
e.g., int getValue(int *result, const my_Object& key);

2. whenever arrays are involved
e.g, int bubbleSort(int *array);

3. whenever you need to express an optional argument
e.g., my_Object::my_Object(int initialValue, an_Allocator = 0);

4. whenever you want to delegate ownership
e.g., objectVector.push_back(new my_Object(22));
void an_Allocator::deallocate(void *buffer);

4. when you want to pass a string
e.g., int lookup(const char *name);
One good style rule is to never return null, and never accept null as
parameter.
This is a naive rule. For example, you want to return a pointer when
it is possible for a result to be invalid, undefined, or unset:

const my_Object *lookup(const char *name);

my_Object *my_Objet::singleton();

Obviously, you need to return by address when you return a dynamically
allocated object, as in a factory method.

As for *accepting* a null pointer argument, there is really no problem
with this as long as you *document* the expected behavior so that your
clients understand how to use your function:

int getValue(int *result, const my_Object& key);
// Load the value associated with the specified 'key'
// into the specified 'result'. Return 0 on success,
// and a non-zero value otherwise. The behavior is
// undefined unless 'result' is a valid pointer. Note
// that the value pointed to by 'result' is not altered
// if the function does not succeed.

Notice that "undefined" behavior means that you, the imlementor, can
deallocate a null or invalid pointer, 'assert' that the parameter is
not null, or whatever.
Follow that rule by passing in instead a Null Object. Consider this code: void funk(SimCity const * pCity)
{
if (pCity)
pCity->throwParade();
} Now contrast with this: class NullCity: public SimCity
{
public: /*virtual*/ void throwParade() {}
}; void funk(SimCity const & aCity)
{
aCity.throwParade();
}
This may be completly unreasonable when working with third-party or
legacy code. Also, it makes it impossible to detect a usage violation
without doing a dynamic_cast on an argument with a polymorphic type.
For example, if it is not valid to pass a null SimCity object to
'funk', you should pass by address and 'assert' that the parameter is
not null. If it is not *possible* to pass a null SimCity object to
'funk', you should pass by reference, and catch errors at compile
time.
C++ supports qualifications before their qualified types, such as "const
SimCity &". I try to write expressions with the most important part first.
There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.


What technical reasons? Style is not "technical"...

/david
Jul 22 '05 #14

P: n/a
> > One good style rule is to never return null, and never accept null as
parameter.
David Rubin did not know his peril when he uttered:
This is a naive rule. For example, you want to return a pointer when
it is possible for a result to be invalid, undefined, or unset:
All rules are naive. Calling that rule naive is naive.
const my_Object *lookup(const char *name);

my_Object *my_Objet::singleton();
You need to study Barton & Nackman's Fallible<> class template, from their
/Scientific & Engineering in C++ something/ book.
Obviously, you need to return by address when you return a dynamically
allocated object, as in a factory method.
That is non-obvious. If the factory throws when it can't allocate, it can't
return NULL, hence the reason for pointing goes away. It could honestly
return a reference.

Rule 1: Prefer references to pointers unless you need pointers' special
abilities.

Rule 2: Avoid the need for pointers' special abilities.

I recently wrote a dialog box, in WTL (on MS Windows) that stores customer
names in a list box, and names and address in edit fields. The edit field
for the State is a combo box. The dialog box stores the name list in XML
(via MSXML via COM), and the list box displays tabs correctly as columns.
The edit fields link to the XML via MVC. The dialog box can localize to
Sanskrit, and can display a cancellable progress bar based on a window
timer.

The only * in the program are for passing constant strings into low-level
functions, and inside one auto_ptr<>. Otherwise, no pointers. (And remember
WTL is a healthier library than MFC!)
As for *accepting* a null pointer argument, there is really no problem
with this as long as you *document* the expected behavior so that your
clients understand how to use your function:

int getValue(int *result, const my_Object& key);
// Load the value associated with the specified 'key'
// into the specified 'result'. Return 0 on success,
// and a non-zero value otherwise. The behavior is
// undefined unless 'result' is a valid pointer. Note
// that the value pointed to by 'result' is not altered
// if the function does not succeed.
Comments suck. int result should be a full-fledged object that enforces
those behaviors, if they are important.
void funk(SimCity const & aCity)
{
aCity.throwParade();
}


This may be completly unreasonable when working with third-party or
legacy code.


It may be the only salvation for such code. Obviously you can't change
someone's opaque function.

When un-f***ing-up legacy code, the ability to slip new polymorphic
behaviors into its tangled code is priceless. Introducing the ability to
polymorph a SimCity carries many more benefits than just introducing
NullObjects.

(Read /working effectively with legacy code/ by Mike Feathers.)
Also, it makes it impossible to detect a usage violation
without doing a dynamic_cast on an argument with a polymorphic type.
For example, if it is not valid to pass a null SimCity object to
'funk', you should pass by address and 'assert' that the parameter is
not null. If it is not *possible* to pass a null SimCity object to
'funk', you should pass by reference, and catch errors at compile
time.


That sounds like an argument for NullObject.

Compile time checking only catches some errors. All a NULL pointer needs to
accidentally become an undefinable reference is a single dereferencing star
* in the wrong spot.

If you are that frantic about them, use wall-to-wall unit tests. BTW my
Sanskrit dialog was written via test-first on every single feature. The test
cases can also record screen shots of the dialog's various locale skins, and
can record animations of the progress bar.
C++ supports qualifications before their qualified types, such as "const
SimCity &". I try to write expressions with the most important part first. There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.


What technical reasons? Style is not "technical"...


No shit. Style is not technical? Damn, am I ever glad I clicked on this post
today! Woah, you sure set me straight on that one!

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces
Jul 22 '05 #15

P: n/a
In message <82*************************@posting.google.com> , David Rubin
<da********@warpmail.net> writes
"Phlip" <ph*******@yahoo.com> wrote in message
news:<MC**************@newssvr33.news.prodigy.com >...
DaKoadMunky wrote:
> >There are 2 and only 2 situations in which I choose a pointer over a
> >reference:
> >
> >1) When it must be re-seated
> >
> >2) When arrays are involved
>
> Another possibility is when a function argument is optional. Pointers can be
> null whereas references cannot.


But default-constructed "null" objects can often be passed by reference
to give the same effect.
Typically, you should pass parameters *by* *address*

1. whenever the parameter is going to be modified
e.g., int getValue(int *result, const my_Object& key);
But a pointer might be null or point to something that's been deleted.
If you pass by non-const reference, you don't have to worry about that.
2. whenever arrays are involved
e.g, int bubbleSort(int *array);
Well, yes, but why use arrays? (why write your own bubblesort? why not
pass two iterators to it? ...)
3. whenever you need to express an optional argument
e.g., my_Object::my_Object(int initialValue, an_Allocator = 0);
Except when it's more appropriate to pass it by value. Or reference
(e.g. see how the STL passes allocators to constructors.)
4. whenever you want to delegate ownership
e.g., objectVector.push_back(new my_Object(22));
void an_Allocator::deallocate(void *buffer);
void* ? Real allocators mostly use Allocator::pointer.

4. when you want to pass a string
e.g., int lookup(const char *name);

Same as the array case. But why use array of const char when you have
std::string?
--
Richard Herring
Jul 22 '05 #16

P: n/a
"Phlip" <ph*******@yahoo.com> wrote in message news:<69*****************@newssvr17.news.prodigy.c om>...
One good style rule is to never return null, and never accept null as
parameter.
David Rubin did not know his peril when he uttered:
This is a naive rule. For example, you want to return a pointer when
it is possible for a result to be invalid, undefined, or unset:


All rules are naive. Calling that rule naive is naive.
const my_Object *lookup(const char *name);

my_Object *my_Objet::singleton();


You need to study Barton & Nackman's Fallible<> class template, from their
/Scientific & Engineering in C++ something/ book.
Obviously, you need to return by address when you return a dynamically
allocated object, as in a factory method.


That is non-obvious. If the factory throws when it can't allocate, it can't
return NULL, hence the reason for pointing goes away. It could honestly
return a reference.


If your function throws an exception, it doesn't matter what it
returns; you need to deal with an exception. There are also situations
in which you do not want to support exceptions (possibly due to
run-time considerations). Furthermore, it is a good idea to follow the
rule that if you have a pointer, you should use a pointer. For
example, what do you gain from this?

Type& my_Factory::allocate() {
Type *obj = new Type;
return *obj;
}

You are also left with the connundrum of writing the analogous
'deallocate' method:

void my_Factory::deallocate(Type& obj);
// 'obj' is altered even though it is passed by reference
and
factory.deallocate(object); // unconventional

or

void my_Factory::deallocate(Type *obj); // not symmetric to
'allocate'
and
factory.deallocate(&object);
// How do you know 'object' is dynamically allocated?

[snip]
As for *accepting* a null pointer argument, there is really no problem
with this as long as you *document* the expected behavior so that your
clients understand how to use your function:

int getValue(int *result, const my_Object& key);
// Load the value associated with the specified 'key'
// into the specified 'result'. Return 0 on success,
// and a non-zero value otherwise. The behavior is
// undefined unless 'result' is a valid pointer. Note
// that the value pointed to by 'result' is not altered
// if the function does not succeed.


Comments suck. int result should be a full-fledged object that enforces
those behaviors, if they are important.


Really? What are the semantics of this function?

my_Result compute(my_Value& v1, my_Value& v2);

Comments are the *only* way most clients can understand how code
works. Most people speak better English (for example) than C++.

C++ supports qualifications before their qualified types, such as "const
SimCity &". I try to write expressions with the most important part first. There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.


What technical reasons? Style is not "technical"...


No shit. Style is not technical? Damn, am I ever glad I clicked on this post
today! Woah, you sure set me straight on that one!


Can you please tell us what you had in mind when you wrote the
original comment, rather than unleash your misguided sarcasm? Several
people here seem interested.

/david
Jul 22 '05 #17

P: n/a
Richard Herring <ju**@[127.0.0.1]> wrote in message news:<qu**************@baesystems.com>...

[snip]
Typically, you should pass parameters *by* *address*

1. whenever the parameter is going to be modified
e.g., int getValue(int *result, const my_Object& key);


But a pointer might be null or point to something that's been deleted.
If you pass by non-const reference, you don't have to worry about that.


You are suggesting

int getValue(int& result, const my_Object& key);

which would be called like this

int rc = getValue(numBeans, pod);

This does not read well since it is difficult to tell what parameter
is being modified. See C++PL 3ed 5.5.

[snip]
4. whenever you want to delegate ownership
e.g., objectVector.push_back(new my_Object(22));
void an_Allocator::deallocate(void *buffer);


void* ? Real allocators mostly use Allocator::pointer.


Yes, this is a typo. And, this is not an 'Allocator', it's
'an_Allocator'. But the point is the same; you still return a pointer.
4. when you want to pass a string
e.g., int lookup(const char *name);

Same as the array case. But why use array of const char when you have
std::string?


This is a subtle point. You want to choose a lowest common
denomentator type. For example, an interface such as

int lookup(const std::string& name);

*forces* clients to use 'std::string'; it's not optional. You client
might prefer to use 'Acme::string', or might not be able to use STL.
(This does happen sometimes...). In any case, with the 'const char *'
interface, clients *can* use 'std::string' or any other string type
which converts to 'const char *'.

This begs the question of whether or not you should *return* a
'std::string' (reference) or a 'const char *'... /david
Jul 22 '05 #18

P: n/a
David Rubin wrote:
If your function throws an exception, it doesn't matter what it
returns; you need to deal with an exception. There are also situations
in which you do not want to support exceptions (possibly due to
run-time considerations). Furthermore, it is a good idea to follow the
rule that if you have a pointer, you should use a pointer. For
example, what do you gain from this?

Type& my_Factory::allocate() {
Type *obj = new Type;
return *obj;
}

You are also left with the connundrum of writing the analogous
'deallocate' method:

void my_Factory::deallocate(Type& obj);
// 'obj' is altered even though it is passed by reference
and
factory.deallocate(object); // unconventional

or

void my_Factory::deallocate(Type *obj); // not symmetric to
'allocate'
and
factory.deallocate(&object);
// How do you know 'object' is dynamically allocated?


Add a smart shared pointer to the above - if your design truly needs 'new'.
Comments suck. int result should be a full-fledged object that enforces
those behaviors, if they are important.


Really? What are the semantics of this function?

my_Result compute(my_Value& v1, my_Value& v2);

Comments are the *only* way most clients can understand how code
works. Most people speak better English (for example) than C++.


I would work on making that function's unit test self-documenting before
working on its comments. You can't test a comment.
C++ supports qualifications before their qualified types, such as "const
SimCity &". I try to write expressions with the most important part
first.
There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.


I'm not sure but there might be more reasons than this:

#define datatype someType*
//typedef someType * datatype;

Only the post-fixed 'const' works the same for each of those two different
ways to declare datatype.

Insert the standard newsgroup screed against #define here.

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces

Jul 22 '05 #19

P: n/a
In message <82*************************@posting.google.com> , David Rubin
<da********@warpmail.net> writes
Richard Herring <ju**@[127.0.0.1]> wrote in message
news:<qu**************@baesystems.com>...

[snip]
>Typically, you should pass parameters *by* *address*
>
>1. whenever the parameter is going to be modified
> e.g., int getValue(int *result, const my_Object& key);
But a pointer might be null or point to something that's been deleted.
If you pass by non-const reference, you don't have to worry about that.


You are suggesting

int getValue(int& result, const my_Object& key);

which would be called like this

int rc = getValue(numBeans, pod);

This does not read well since it is difficult to tell what parameter
is being modified. See C++PL 3ed 5.5.


....which says, inter alia, 'Consequently "plain" reference arguments
should be used only where the name of the function gives a strong hint
that the reference argument is modified.'

"Get" in the function's name _would_ be such a hint were it not for the
fact that the function returns _another_ value as its return value. I'd
be inclined to question why. If it's an error indicator, perhaps you'd
be better throwing an exception instead of returning a code which has to
be tested.
[snip]
>4. whenever you want to delegate ownership
> e.g., objectVector.push_back(new my_Object(22));
> void an_Allocator::deallocate(void *buffer);
void* ? Real allocators mostly use Allocator::pointer.


Yes, this is a typo. And, this is not an 'Allocator', it's
'an_Allocator'. But the point is the same; you still return a pointer.
>4. when you want to pass a string
> e.g., int lookup(const char *name);
>

Same as the array case. But why use array of const char when you have
std::string?


This is a subtle point. You want to choose a lowest common
denomentator type. For example, an interface such as

int lookup(const std::string& name);

*forces* clients to use 'std::string'; it's not optional. You client
might prefer to use 'Acme::string',


So they have to write lookup(std::string(acmeString.c_str())). That's
not so terrible.
or might not be able to use STL.
If we're talking about standard (hosted) C++, I think that possibility
is somewhat academic.
(This does happen sometimes...). In any case, with the 'const char *'
interface, clients *can* use 'std::string' or any other string type
which converts to 'const char *'.
But now they can't pass a string which contains '\0'. This happens
sometimes, too.
This begs the question of whether or not you should *return* a
'std::string' (reference) or a 'const char *'... /david


Neither. Return a std::string by value and you have no need to argue
about ownership or lifetime of the result.
--
Richard Herring
Jul 22 '05 #20

P: n/a
>But a pointer might be null or point to something that's been deleted.
If you pass by non-const reference, you don't have to worry about that.


Careless coding could lead to a reference that refers to a deleted object.

void SomeFunc(int)
{
}

int main()
{
int * intPtr = new int(0);
int & intRef = *intPtr;

delete intPtr;

SomeFunc(intRef);

return 0;
}

And of course there is also the "returning a reference to a local" that I think
is discussed in the FAQ.


Jul 22 '05 #21

P: n/a
In message <20***************************@mb-m11.aol.com>, DaKoadMunky
<da*********@aol.com> writes
But a pointer might be null or point to something that's been deleted.
If you pass by non-const reference, you don't have to worry about that.
Careless coding could lead to a reference that refers to a deleted object.


But you have to use pointers to achieve it.
void SomeFunc(int)
{
}

int main()
{
int * intPtr = new int(0);
int & intRef = *intPtr;

delete intPtr;

SomeFunc(intRef);

return 0;
}

And of course there is also the "returning a reference to a local" that I think
is discussed in the FAQ.

Indeed. But here we're talking about passing references _in_, not _out_.
--
Richard Herring
Jul 22 '05 #22

P: n/a
"Phlip" <ph*******@yahoo.com> wrote in message news:<tN***************@newssvr19.news.prodigy.com >...

There are also subtle technical reasons, in rare situations, to write
"SimCity const &", with the const after its type.


There's always:

char const* const days[] =
{
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

The rule is that a cv-qualifier modifies the thing to its left, unless
it's at the beginning of the type specifier in which case it modifies
the thing to its right.

Since it is sometimes necessary to put the const after the thing it
modifies (as in the second const above), it is reasonable to argue
that one should *always* do so, for reasons of consistency.

There's also a pedalogical argument for the put-const-after style.
The habit of putting const first creates the misleading impression
that const modifies the thing to its right, wheras this is actually
the exception. One must unlearn this in order to correctly write
declarations like the one above.

Despite all of the above, putting const first is certainly the more
conventional style. See the C++ standard document for example.
Doing otherwise just "feels wrong" to me. Perhaps it has something
to do with the fact that adjectives in English precede the nouns
they modify, or maybe old habits just die hard.

BTW, thanks for the interesting discussion about null pointers. I've
found the arguments quite stimulating on both sides.
Jul 22 '05 #23

P: n/a
"Phlip" <ph*******@yahoo.com> wrote in message news:<tN***************@newssvr19.news.prodigy.com >...

[snip]
Comments suck. int result should be a full-fledged object that enforces
those behaviors, if they are important.


Really? What are the semantics of this function?

my_Result compute(my_Value& v1, my_Value& v2);

Comments are the *only* way most clients can understand how code
works. Most people speak better English (for example) than C++.


I would work on making that function's unit test self-documenting before
working on its comments. You can't test a comment.


Firstly, how can you write a unit test if you don't specify the
semantics of the function? Secondly, how do you expect clients of your
software to understand how it works? By reading source code? No. They
will look at the header file, since that, and the library, is all they
have.

So, in fact, you *only* test comments! For example,

bool operator<(const IPv4Address& lhs, const IPv4Address& rhs);

What does this function do?

1. Behavior

// Return true if the 'lhs' object is less than the 'rhs'
object,
// and false otherwise.

Okay, how do you construct a unit test around this behavioral
description? What values should you choose? What should the unit test
look like?

2. Definition of Terms

// 'lhs' is less than 'rhs' if its address is less than that
// of 'rhs', or if its address is equal to that of 'rhs' and
// its port number is less than that of 'rhs'.

This is a lot clearer. Now we can at least choose test values and know
what result to expect. But why should 'IPv4Address' objects be ordered
in this way? It's rather arbitrary. We could just as easily have
ordered them by port number. In fact, some clients might prefer such
an ordering if they want to group "connections" by their service
endpoint (i.e., group 'IPv4Address' objects representing connections
to various machines by port number). Well, your clients have a right
to know your motivations.

3. Note That

// Note that this arbitrary ordering of 'IPv4Address' objects
// is chosen primariliy to facilitate interoperability with
// STL Sorted Associative Containers.

Now that you've sufficiently *documented* the semantics of your
function, and your motivations, you can test your function:

Unit Test (in a separate .cpp file with a 'main')

// TESTING LESS THAN (<) OPERATOR
//
// Concerns:
// Subtle differences in IP address and port number of two
'IPv4Address'
// objects are detected by comparison with the less-than
operator.
//
// Plan:
// Specify a set S of 'IPv4Address' objects each having
variations in
// the IP address and/or port number. Compare each pair (u, v)
in the
// cross product S x S, such that u != v.
//
// Create a 'std::vector', 'W', of 'IPv4Address' objects that
acts as a
// control. Create a copy of 'W' named 'X'. Randomly permute
'X', and
// verify that 'X' is different than 'W'. Sort 'X' using
'std::sort',
// implicitly exercising the defined less-than operator, and
verify that
// 'X' is the same as 'W'.
//
// Testing:
// bool operator<(const IPv4Address& lhs, const IPv4Address&
rhs);

Okay. Now you know exactly the semantics of the function and the
structure of the unit test. You also know that your motivations are
validated by a concrete example. I have not written *any* code. The
function and the test case are *documented*, not "self documenting."

/david
Jul 22 '05 #24

P: n/a
On Sun, 25 Jul 2004 22:30:55 GMT, Desmond Liu <dl************@shaw.ca>
wrote:
I've read articles like Scott Meyer's EC++ (Item 22) that advocate the use
of references when passing parameters. I understand the reasoning behind
using references--you avoid the cost of creating a local object when you
pass an object by reference.

But why use a reference? Is there any inherent advantage of using a
reference over using a pointer? My workplace discourages the use of
pointers when it comes to parameter passing. They always prefer references.

[snip]

There are always situations where one will be preferred over the
other, but in general, go with references. A reference will always
point to a valid object (unless there is evil trickery at play),
whereas a pointer might not. And aside from checking for NULL, the
function has no way of knowing whether the pointer is valid or not.

An example of "evil trickery":

class A{};
void f(A&){}
int main()
{
A* pa = new A();
A& ra = *pa;
delete pa;
f(ra);
return 0;
}

This should never happen in real life. Unfortunately, it sometimes
does ... but passing pointers instead of references isn't going to
help you when it does happen.

--
Bob Hairgrove
No**********@Home.com
Jul 22 '05 #25

P: n/a
>An example of "evil trickery":

class A{};
void f(A&){}
int main()
{
A* pa = new A();
A& ra = *pa;
delete pa;
f(ra);
return 0;
}


Isn't it possible that this could also occur though not out of malice but just
carelessness?

Suppose many statements separated the statements presented and our poor little
programmer got confused.

It does seem reasonable that the ordering of the delete and the use of the
reference could just be accidental.

I guess I am just being picky about phrasing.
Jul 22 '05 #26

P: n/a
Bob Hairgrove posted:
On Sun, 25 Jul 2004 22:30:55 GMT, Desmond Liu <dl************@shaw.ca> wrote:
I've read articles like Scott Meyer's EC++ (Item 22) that advocate theuse of references when passing parameters. I understand the reasoningbehind using references--you avoid the cost of creating a local objectwhen you pass an object by reference.

But why use a reference? Is there any inherent advantage of using areference over using a pointer? My workplace discourages the use ofpointers when it comes to parameter passing. They always preferreferences.
[snip]

There are always situations where one will be preferred

over the other, but in general, go with references. A reference will always point to a valid object (unless there is evil trickery at play), whereas a pointer might not. And aside from checking for NULL, the function has no way of knowing whether the pointer is valid or not.
An example of "evil trickery":

class A{};
void f(A&){}
int main()
{
A* pa = new A();
A& ra = *pa;
delete pa;
f(ra);
return 0;
}

This should never happen in real life. Unfortunately, it sometimes does ... but passing pointers instead of references isn't going to help you when it does happen.

--
Bob Hairgrove
No**********@Home.com

That's not evil, this is:
int& candy(*reintepret_cast<int*>(8742));

candy = 5;
-JKop
Jul 22 '05 #27

This discussion thread is closed

Replies have been disabled for this discussion.