views:

223

answers:

7

A very basic question, but still, it would be good to hear from C++ gurus out there.

There are two rather similar ways to declare by-reference parameters in C++.

1) Using "asterisk":

void DoOne(std::wstring* iData);

2) Using "ampersand":

void DoTwo(std::wstring& iData);

What are implications of each method? Are there any gotcha's in any case?

Bonus #1: What would be a formal way to call method in #1 and #2? Are they both called "by-reference"?

Bonus #2: std::wstring is used deliberately. What would be implications towards standard library classes in each case?

A: 

While writing examples, I came up with my own answer. Anything other than below?

The result of each of them is quite similar: a reference to an object in the memory ends up within method's scope. There seem to be no strict memory requirements for any of them. The object can be either on stack or in heap.

In case of stack each of the methods would be called like this:

{
    std::wstring data;
    DoOne(&data);
    DoTwo(data);
}

Yet, when it comes to the heap, the second approach would require that the object must exist before calling the method. If the object does not exist, the caller would cause exception, not the callee.

{
    std::wstring* pData = new std::wstring();
    DoOne(pData);
    DoTwo(*pData);
}

In the above, if out-of-memory condition occurs and pData ends up NULL, the crash would happen before DoTwo, but DoOne would swallow the NULL and might crash some time later.

Ignas Limanauskas
+1 Not much to add :)
Magnus Skog
There is! How each method is called formally?
Ignas Limanauskas
DoOne is pass-by-reference, DoTwo is pass-by-pointer. References cannot be NULL, it is enforced by the compiler. So your statement "If the object does not exist, the caller would cause exception" is wrong. Also, new cannot return NULL. If it fails, it throws an exception. So you don't need to worry. Pass-by-reference is preferred in most cases.
rlbond
The compiler needn't stop a references being null if you (invalidly) dereference a null pointer. I'd expect with most compilers the crash will occur in the callee when the object is used. Anything else requires the compiler to insert a null check in the caller, which could be costly. But you're right that new doesn't return null, unless you're on some pre-standard compiler like Symbian. nothrow new would illustrate the issue.
Steve Jessop
pData will never be NULL. In low memory situations new will throw an exception.
Martin York
pData may very well be 0 if you instruct your compiler to disable exceptions. Dereferencing a NULL pointer is not an error, but doing anything with it is undefined. Generally, it will act the same as a NULL pointer.
Dolphin
A: 

I wouldnt call myself a C++ gure (except for on my CV), but i'd say; unless theres any use of passing the parameter as a pointer (i.e the function wants to check for null), always use a reference.

That also goes for functions returning objects, returning a pointer is somehow telling the user of the class that it might be null.

Viktor Sehr
A: 

In DoOne, iData can be assigned NULL. If you use that after calling DoOne, the application will crash.

Something like

void DoOne(std::wstring* iData)
{
   //Use iData
   delete iData;
   iData = NULL;
}

and

{
    std::wstring* pData = new std::wstring();
    DoOne(pData);
    pData->someFunction(); //Crash
}
Amol Gawai
It would crash because the object was deleted, not because the pointer was assigned to NULL.
James Hopkin
A: 

Your answer is completely wrong when you say:

The result of each of them is quite similar: a reference to an object in the memory ends up within method's scope. There seem to be no strict memory requirements for any of them.

connsider:

void f( int * p1 ) {
   int ** p2 = & p1;
}

here p1 has a definite "memory requirement" - it must exist and I must be able to take its address. Contrast this with

void f( int & r ) }
   int * p = & r;
}

here r has no existence of its own, it is merely a reference. Whem I take its address I am taking the address of the thing that r refers to.

Your remarks regarding the NULL pointer are also mistaken. Derefrencing the NULL pointer causes undefined behaviour - this may or may not result in a crash.

anon
Do you mean to say that in C++ there exists no such thing as "Pass By Pointer"? Though you're right, I think the question is about when to _use_ a pointer or a reference.
xtofl
No, I don't mean to say that.
anon
+3  A: 

#1 uses a pointer parameter ('passing a pointer to'), #2 uses a reference parameter ('passing by reference'). They are very similar, but note that the calling code looks different in the two cases:

std::wstring s;

DoOne(&s); // pass a pointer to s
DoTwo(s); // pass s by reference

Some people prefer #1, using a convention that passing by pointer indicates that the function might change the value of s (even though either function could). Other people (myself included) prefer #2, since passing by reference does not allow NULL to be passed.

There is another important difference when passing by const pointer or reference. A temporary variable can only be passed to a const reference parameter:

void ByConstPointer(const std::wstring&);
void ByConstReference(const std::wstring*);

void test()
{
  ByConstPointer(&std::wstring(L"Hello")); // error: cannot take address of temporary
  ByConstReference(std::wstring(L"Hello")); // fine
}
James Hopkin
+1 for the temporary variable!
Ignas Limanauskas
A: 

If you write a function that gets a variable by pointer, you'll most probably have to check if the pointer is valid (eg not NULL), else you risk program crash.

HMage
"you risk program crash". Standard functions like strcpy don't check for null. They take the view that it's their caller who is risking a crash (by passing an invalid value), not them.
Steve Jessop
+1  A: 

Rule number one for this: If NULL is a valid value for the function parameter in the context of the function, then pass it as pointer, otherwise pass it as reference.

Rationale, if it cannot (should not!) ever be NULL, then don't put yourself through the trouble of checking for NULL.

Johann Gerell