收录日期:2021/01/23 07:14:22 时间:2010-03-31 04:11:53 标签:c++

I can imagine one case, in which the input parameter could be NULL so that pass-by-pointer is preferred but not pass-by-reference?

Can anybody add more cases?

Some like pass-by-pointer better in cases where the object being passed is actually going to be modified. They use pass-by-const-reference when the object is being passed by reference in order to avoid a copy of the object, but will not be changed in the function.

In illustration, take the following functions:

int foo(int x);
int foo1(int &x);
int foo2(int *x);

Now in the code, I do the following:

int testInt = 0;

foo(testInt);   // can't modify testInt
foo1(testInt);  // can modify testInt

foo2(&testInt); // can modify testInt

In calling foo vs foo1, it's not apparent from the callers perspective (or a programmer reading the code) that the function can modify testInt without having to look at the signature of the function. Looking at foo2, a reader can easily see that the function may in fact modify the value of testInt because the function is receiving the address of the parameter. Note that this doesn't guarantee the object is actually modified, but that's where being consistent in the use of references vs. pointers helps. In general, if you want to follow this guideline consistently you should always pass const references when you want to avoid copies, and pass by pointer when you want to be able to modify the object.

The C++ FAQ has a very good answer for this question:

Use references when you can, and pointers when you have to.

References are usually preferred over pointers whenever you don't need "reseating". This usually means that references are most useful in a class's public interface. References typically appear on the skin of an object, and pointers on the inside.

The exception to the above is where a function's parameter or return value needs a "sentinel" reference — a reference that does not refer to an object. This is usually best done by returning/taking a pointer, and giving the NULL pointer this special significance (references should always alias objects, not a dereferenced NULL pointer).

Note: Old line C programmers sometimes don't like references since they provide reference semantics that isn't explicit in the caller's code. After some C++ experience, however, one quickly realizes this is a form of information hiding, which is an asset rather than a liability. E.g., programmers should write code in the language of the problem rather than the language of the machine.

You have many situations in real world programming wherein a parameter does not exist or is invalid and this can depend on runtime semantics of the code. In such situations you can use NULL (0) to signal this state. Apart from this,

  • A pointer can be re-assigned to a new state. A reference cannot. This is desirable in some situations.
  • A pointer helps transfer owner-ship semantics. This is especially useful in multi-threaded environment if the parameter-state is used to execute in a separate thread and you do not usually poll till the thread has exited.

Although if enough time is spent designing the code properly, these situations can be avoided; in practice it is not possible everytime.

In addition to some other answers about ownership semantics (especially factory functions).

While not a technical reason, a common style guide requirement is that any parameters that may be modified should be passed by pointer. This makes it obvious at the callsite that the object may be modified.

void Operate(const int &input_arg, int *output_arg) {
  *output_arg = input_arg + 1;
}

int main() {
  int input_arg = 5;
  int output_arg;
  Foo(input_arg, &output_arg);  // Passing address, will be modified!
}

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 or risking problems due to it being NULL.

When dealing with raw memory (for example if creating your own memory pool), you'd want to use a pointer. But you're right, in normal code the only use for a pointer is an optional parameter.

In C++ there's little to no need to pass by pointer in most cases. You should consider first the alternatives: templates, (const) references, containers, strings and smart pointers. That said, if you must support legacy code then you'll need to use pointers. If your compiler is minimalistic (embedded systems, for example) you'll need pointers. If you need to talk to a C library (a system library of some sort for a very particular driver you're working with?) then you'll need to work with pointers. If you want deal with very particular offsets of memory then you'll need pointers.

In C pointers are first-class citizens, they are way too fundamental to think about eliminating them.

Anytime you pass a function pointer. Or if you have a "reset" method a la auto_ptr.

This isn't specifically argument passing, but it does affect argument passing.

You can have a container/aggregate of pointers, but not of references. Although references are polymorphic, only pointers support the "update" operation which is essential to container use (especially since you can't yet initialize references en masse, not sure if C++0x aggregate initializers will change that).

So if you have a container full of pointers, you will usually end up managing it with functions that accept pointers rather than references.

When you need to manipulate (e.g. resize, reassign, etc.) the address being pointed to by the pointer inside the function, then you need a pointer.

For example:


void f( int* p )
{
    // ..

    delete []p;
    p = new int[ size ];

    //...
}

Unlike references, values of pointers can be changed and manipulated.

A function could, conceivably, be written to do something with the memory, such as reallocating it to make it larger (returning the new pointer).

If I need to pass an array of objects, I need to pass by pointer. Arrays aren't always stored in std::vector<>.

Other than that, pass by pointer allows NULL and pass by reference doesn't, so I use that distinction as a contract much like NULL vs. NOT NULL columns in SQL, and loosely like "function returning bool: true = succeeded and false = failed, vs. function returning int: return value is a result code where 0 = success and others are failures".

Google seems to have a strong opinion on this, and I tend to agree:

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments