When to use references vs. pointers

2018-12-31 07:13发布

I understand the syntax and general semantics of pointers versus references, but how should I decide when it is more-or-less appropriate to use references or pointers in an API?

Naturally some situations need one or the other (operator++ needs a reference argument), but in general I'm finding I prefer to use pointers (and const pointers) as the syntax is clear that the variables are being passed destructively.

E.g. in the following code:

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

With the pointer, it's always (more) obvious what's going on, so for APIs and the like where clarity is a big concern are pointers not more appropriate than references? Does that mean references should only be used when necessary (e.g. operator++)? Are there any performance concerns with one or the other?

EDIT (OUTDATED):

Besides allowing NULL values and dealing with raw arrays, it seems the choice comes down to personal preference. I've accepted the answer below that references Google's C++ Style Guide, as they present the view that "References can be confusing, as they have value syntax but pointer semantics.".

Due to the additional work required to sanitise pointer arguments that should not be NULL (e.g. add_one(0) will call the pointer version and break during runtime), it makes sense from a maintainability perspective to use references where an object MUST be present, though it is a shame to lose the syntactic clarity.

19条回答
看风景的人
2楼-- · 2018-12-31 07:26

Any performance difference would be so small that it wouldn't justify using the approach that's less clear.

First, one case that wasn't mentioned where references are generally superior is const references. For non-simple types, passing a const reference avoids creating a temporary and doesn't cause the confusion you're concerned about (because the value isn't modified). Here, forcing a person to pass a pointer causes the very confusion you're worried about, as seeing the address taken and passed to a function might make you think the value changed.

In any event, I basically agree with you. I don't like functions taking references to modify their value when it's not very obvious that this is what the function is doing. I too prefer to use pointers in that case.

When you need to return a value in a complex type, I tend to prefer references. For example:

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

Here, the function name makes it clear that you're getting information back in an array. So there's no confusion.

The main advantages of references are that they always contain a valid value, are cleaner than pointers, and support polymorphism without needing any extra syntax. If none of these advantages apply, there is no reason to prefer a reference over a pointer.

查看更多
妖精总统
3楼-- · 2018-12-31 07:26

Pointers and references have equal speed, and pointers can do everything references can and more. It is heavily based on personal opinion which one to use. It's common to use references unless you really need some of the features of pointers.

查看更多
闭嘴吧你
4楼-- · 2018-12-31 07:28

There is problem with "use references wherever possible" rule and it arises if you want to keep reference for further use. To illustrate this with example, imagine you have following classes.

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

At first it may seem to be a good idea to have parameter in RefPhone(const SimCard & card) constructor passed by a reference, because it prevents passing wrong/null pointers to the constructor. It somehow encourages allocation of variables on stack and taking benefits from RAII.

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

But then temporaries come to destroy your happy world.

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

So if you blindly stick to references you trade off possibility of passing invalid pointers for the possibility of storing references to destroyed objects, which has basically same effect.

edit: Note that I sticked to the rule "Use reference wherever you can, pointers wherever you must. Avoid pointers until you can't." from the most upvoted and accepted answer (other answers also suggest so). Though it should be obvious, example is not to show that references as such are bad. They can be misused however, just like pointers and they can bring their own threats to the code.


There are following differences between pointers and references.

  1. When it comes to passing variables, pass by reference looks like pass by value, but has pointer semantics (acts like pointer).
  2. Reference can not be directly initialized to 0 (null).
  3. Reference (reference, not referenced object) can not be modified (equivalent to "* const" pointer).
  4. const reference can accept temporary parameter.
  5. Local const references prolong the lifetime of temporary objects

Taking those into account my current rules are as follows.

  • Use references for parameters that will be used locally within a function scope.
  • Use pointers when 0 (null) is acceptable parameter value or you need to store parameter for further use. If 0 (null) is acceptable I am adding "_n" suffix to parameter, use guarded pointer (like QPointer in Qt) or just document it. You can also use smart pointers. You have to be even more careful with shared pointers than with normal pointers (otherwise you can end up with by design memory leaks and responsibility mess).
查看更多
冷夜・残月
5楼-- · 2018-12-31 07:28

For pointers, you need them to point to something, so pointers cost memory space.

For example a function that takes an integer pointer will not take the integer variable. So you will need to create a pointer for that first to pass on to the function.

As for a reference, it will not cost memory. You have an integer variable, and you can pass it as a reference variable. That's it. You don't need to create a reference variable specially for it.

查看更多
素衣白纱
6楼-- · 2018-12-31 07:31

Like others already answered: Always use references, unless the variable being NULL/nullptr is really a valid state.

John Carmack's viewpoint on the subject is similar:

NULL pointers are the biggest problem in C/C++, at least in our code. The dual use of a single value as both a flag and an address causes an incredible number of fatal issues. C++ references should be favored over pointers whenever possible; while a reference is “really” just a pointer, it has the implicit contract of being not-NULL. Perform NULL checks when pointers are turned into references, then you can ignore the issue thereafter.

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

Edit 2012-03-13

User Bret Kuhns rightly remarks:

The C++11 standard has been finalized. I think it's time in this thread to mention that most code should do perfectly fine with a combination of references, shared_ptr, and unique_ptr.

True enough, but the question still remains, even when replacing raw pointers with smart pointers.

For example, both std::unique_ptr and std::shared_ptr can be constructed as "empty" pointers through their default constructor:

... meaning that using them without verifying they are not empty risks a crash, which is exactly what J. Carmack's discussion is all about.

And then, we have the amusing problem of "how do we pass a smart pointer as a function parameter?"

Jon's answer for the question C++ - passing references to boost::shared_ptr, and the following comments show that even then, passing a smart pointer by copy or by reference is not as clear cut as one would like (I favor myself the "by-reference" by default, but I could be wrong).

查看更多
永恒的永恒
7楼-- · 2018-12-31 07:31

In general a member variable should never be a reference because there is no point in that. It causes the class to be non-assignable if you do not provide an assignment operator. Also once you set the member reference to refer to some object, it is not possible to change that member for referring another object. The most appropriate usage of a reference is using as a function parameter which enables pass by reference.

查看更多
登录 后发表回答