51

Common Situations:

  1. Passing std::string to a function foo(std::string*) or foo(std::string&);
  2. Passing tr1::shared_ptr to a function foo(tr1::shared_ptr* ptr) or foo(tr1::shared_ptr& ptr);

In general, what is a good practice. I always get confused. At first, passing everything as references seems consistent, however it is not possible to pass in Literals as references or NULLs as references.

Similarly, having everything as pointers seems good, but having then I have to worry that pointers might be pointing to NULL and check for those conditions in the beginning of that function.

Do you think the following snippet is good?

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <tr1/memory>
#include <algorithm>
using namespace std;
using namespace std::tr1;

int main(){
        map<string, shared_ptr<vector<string> > > adjacencyMap;
        vector<string>* myFriends = new vector<string>();
        myFriends->push_back(string("a"));
        myFriends->push_back(string("v"));
        myFriends->push_back(string("g"));
        adjacencyMap["s"] = shared_ptr<vector<string> >(myFriends);
        return 0;
}

Thanks Ajay

sbi
  • 219,715
  • 46
  • 258
  • 445
user855
  • 19,048
  • 38
  • 98
  • 162
  • `foo(tr1::shared_ptr* ptr)` that just seems very wrong from perspective of good design in my opinion. you do know about constant references? – Anycorn Aug 31 '10 at 20:55
  • 3
    Why don't you put `myFriends` into a smart pointer right away? – GManNickG Aug 31 '10 at 20:55
  • 2
    I think this is a duplicate of [How to pass objects to functions in C++?](http://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c), where I've already answered the general question. – sbi Aug 31 '10 at 20:59
  • You can also NOT use a `shared_ptr` (or a `new`), see my answer... – Matthieu M. Sep 01 '10 at 07:00
  • possible duplicate of [When pass-by-pointer is preferred to pass-by-reference in C++?](http://stackoverflow.com/questions/2550377/when-pass-by-pointer-is-preferred-to-pass-by-reference-in-c) – outis Jan 21 '12 at 00:22

8 Answers8

30

A good rule of thumb: "Use references when you can and pointers when you have to".

Lucas
  • 6,328
  • 8
  • 37
  • 49
29

References are easier to get right.

Is your problem with literals that you aren't using const references? You can't bind a temporary (produced by a literal) to a non-const reference, because it makes no sense to change one. You can bind one to a const reference.

In particular, when passing an argument to a function, and the function isn't going to change it, and it isn't a built-in type, pass by const reference. It works much the same as pass by value, except it doesn't require a copy constructor call.

Pointers are useful in that they have a guaranteed invalid value you can test for. Sometimes this is irrelevant, and sometimes it's very important. Of course, you can't generally pass a literal by pointer, unless (in case of a string literal) it already is.

Some coding standards say that nothing should ever be passed by non-const reference, since it provides no indication at the point of call that the argument might be changed by the function. In that case, you will be required to pass by pointer. I don't favor this, particularly as programming tools make it easier and easier to get the function signature, so you can see if a function might change an argument. However, when working in a group or for an enterprise, style consistency is more important than any individual style element.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
7

In my previous job, we had the the rule that plain references were practically never used. Instead we agreed to:

  • pass by value (for cheap to copy objects, all primitives, small value types, std::string, very small or refcounted strings)
  • pass by const reference (for readonly access to large objects)
  • pass by pointer if you need read-write access

If everybody follows these rules, you can assume that parameters passed to functions are not modified unless their adress was taken. It worked for us.

  • Why pass std::string by value? Should be by const reference. You should also pass by reference by default even when doing read-write access. – ronag Aug 31 '10 at 21:08
  • What if you want to modify a primitive within the function? How does you Point 1 satisfy that case? – user855 Aug 31 '10 at 21:10
  • +1, pretty sensible. @aja pass by pointer (number 3) @ron number 2 says so. "should" is really a matter of preference – Anycorn Aug 31 '10 at 21:15
  • @ronag: If you pass strings, you usually expect pass by value semantics. std::string has special handling of small strings to make copies cheap. Haven't run in a situation where optimizing this helped. –  Aug 31 '10 at 21:27
  • that special handling is implementation specific and i dont think there is any modern implementation that does that anymore since multithreading became more used (i might be wrong). And even if some implementation did that, there is always an overhead... and also what about large strings? – ronag Aug 31 '10 at 21:36
  • 1
    I was assuming refcounting, but after some googling, that appears to be out of fashion. I'll edit. –  Aug 31 '10 at 22:04
5

I really don't understand why you got to all this trouble:

std::map < std::string, std::vector<std::string> > adjacencyMap;
std::vector<std::string>& sFriends = adjacencyMap["s"];
sFriends.push_back("a");
sFriends.push_back("v");
sFriends.push_back("g");

Why do you meddle with shared_ptr here ? The situation certainly does not call for it!

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
3

You can browse http://www.cplusplus.com/forum/beginner/3958/ for some insights. Also useful: http://www.velocityreviews.com/forums/t284603-pointers-vs-references-a-question-on-style.html

I guess there is no "right" answer. You need to weight pros and cons of each approach taking into account the specific context of your project.

I personally prefer references, but I recommend anyways to read those posts and muse about it.

Sebastian
  • 1,835
  • 3
  • 22
  • 34
3

As a general rule of thumb, always try to pass parameters by reference to const. Passing pointers can lead to ownership issues as well as a bunch of other possibilities for subtle mistakes.

What is the purpose of NULL? To indicate an invalid pointer/object. If you're going to pass invalid objects to a function, then all you have to do is have a method for checking an object's validity. As in:

void myfunc(const obj& myobj)
{
  if(myobj.valid())
    // DO SOMETHING
}

Primitive types you'd usually want to pass-by-value anyway, since there's such little overhead. And that's when you'd use literals most of the time anyway. For strings, you should try to use std::string and stay away from const char* C-style strings as much as you can. Of course if you have to use C-strings, then you have no choice but to use pointers, but all in all references should be the way to go.

Oh and to be truly exception-safe try to avoid this:

vector<string>* myFriends = new vector<string>();
...
adjacencyMap["s"] = shared_ptr<vector<string> >(myFriends);

Instead do:

shared_ptr<vector<string> > myFriends(new vector<string>());

Look in to RAII and exception safety for why this is the preferred method.

Zoli
  • 1,137
  • 7
  • 12
3

Probably not an answer to the question. Just KISS.

int main()
{
        multimap<string, string> adjacencyMap;
        adjacencyMap.insert(std::make_pair("s", "a"));
        adjacencyMap.insert(std::make_pair("s", "v"));
        adjacencyMap.insert(std::make_pair("s", "g"));
        return 0;
}
ronag
  • 49,529
  • 25
  • 126
  • 221
  • Thanks! Although this does not answer my question, I didn't know about multimap. And this usage seems very appropriate to this case. – user855 Aug 31 '10 at 21:27
  • On a side, note there seems to be a memory hit in this approach. When using a multimap the same key is stored multiple agains repeatedly. In my appraoch, since keys are unique, they are stored only once and space usage is less. Any thoughts on this? – user855 Aug 31 '10 at 23:15
  • Thats implementation specific. I believe most implementations store only one key once inserted. – ronag Sep 01 '10 at 06:46
  • I don't know about that, when iterating you're given a reference to a pair, they would be hard pressed to return that if they were implementing the optimization you are talking about. – Matthieu M. Sep 01 '10 at 06:57
1

I would prefer

    map<string, shared_ptr<vector<string> > > adjacencyMap;
    shared_ptr<vector<string> > myFriends(new vector<string>());
    myFriends->push_back(string("a"));
    myFriends->push_back(string("v"));
    myFriends->push_back(string("g"));
    adjacencyMap["s"] = myFriends;
    return 0;

as this ensures your local var handling is exception-safe.

I don't really see how this addresses your q which was about the merits of ref vs ptr, though. In both of the examples you cite I would expect to use the second (ref) form.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140