PKua
PKua

Reputation: 473

C++ conventions regarding passing objects (pointer vs reference)

I'd like to work out conventions on passing parameters to functions/methods. I know it's a common issue and it has been answered many times, but I searched a lot and found nothing that fully satisfies me.

Passing by value is obvious and I won't mention this. What I came up with is:

  1. Passing by non-const reference means, that object is MODIFIED
  2. Passing by const reference means, that object is USED
  3. Passing by pointer means, that a reference to object is going to be STORED. Whether ownership is passed or not will depend on the context.

It seems to be consistent, but when I want to pick heap-allocated object and pass it to 2. case parameter, it'd look like this:

void use(const Object &object) { ... }

//...

Object *obj = getOrCreateObject();
use(*obj);

or

Object &obj = *getOrCreateObject();
use(obj);

Both look weird to me. What would you advise?

PS I know that one should avoid raw pointers and use smart instead (easier memory managment and expressiveness in ownership) and it can be the next step in refactoring the project I work on.

Upvotes: 3

Views: 2143

Answers (3)

Peter
Peter

Reputation: 36637

The main problem with your conventions is that you make no allowance for the possibility of interfacing to code (e.g. written by someone else) that doesn't follow your conventions.

Generally speaking, I use a different set of conventions, and rarely find a need to work around them. (The main exception will be if there is a need to use a pointer to a pointer, but I rarely need to do that directly).

Passing by non-const reference is appropriate if ANY of the following MAY be true;

  • The object may be changed;
  • The object may be passed to another function by a non-const reference [relevant when using third party code by developers who choose to omit the const - which is actually something a lot of beginners or lazy developers do];
  • The object may be passed to another function by a non-const pointer [relevant when using third party code be developers who choose to omit the const, or when using legacy APIs];
  • Non-const member functions of the object are called (regardless of whether they change the object or not) [also often a consideration when using third-party code by developers who prefer to avoid using const].

Conversely, const references may be passed if ALL of the following are true;

  • No non-mutable members of the object are changed;
  • The object is only passed to other functions by const reference, by const pointer, or by value;
  • Only const member functions of the object are called (even if those members are able to change mutable members.

I'll pass by value instead of by const reference in cases where the function would copy the object anyway. (e.g. I won't pass by const reference, and then construct a copy of the passed object within the function).

Passing non-const pointers is relevant if it is appropriate to pass a non-const reference but there is also a possibility of passing no object (e.g. a nullptr).

Passing const pointers is relevant if it is appropriate to pass a const reference but there is also a possibility of passing no object (e.g. a nullptr).

I would not change the convention for either of the following

  • Storing a reference or pointer to the object within the function for later use - it is possible to convert a pointer to a reference or vice versa. And either one can be stored (a pointer can be assigned, a reference can be used to construct an object);
  • Distinguishing between dynamically allocated and other objects - since I mostly either avoid using dynamic memory allocation at all (e.g. use standard containers, and pass them around by reference or simply pass iterators from them around) or - if I must use a new expression directly - store the pointer in another object that becomes responsible for deallocation (e.g. a std::smart_pointer) and then pass the containing object around.

Upvotes: 1

eerorika
eerorika

Reputation: 238471

You can use these conventions if you like. But keep in mind that you cannot assume conventions when dealing with code written by other people. You also cannot assume that people reading your code are aware of your conventions. You should document an interface with comments when it might be ambiguous.

Passing by pointer means, that object is going to be STORED. Who's its owner will depend on the context.

I can think of only one context where the ownership of a pointer argument should transfer to the callee: Constructor of a smart pointer.

Besides possible intention of storing, a pointer argument can alternatively have the same meaning as a reference argument, with the addition that the argument is optional. You typically cannot represent an optional argument with a reference since they cannot be null - although with custom types you could use a reference to a sentinel value.

Both look weird to me. What would you advise?

Neither look weird to me, so my advise is to get accustomed.

Upvotes: 1

volperossa
volperossa

Reputation: 1431

In my opionion, they are the same. In the first part of your post, you are talking about the signature, but your example is about function call.

Upvotes: 0

Related Questions