3

I am always in a doubt on when I should reference and when I should use a variable pass. For example, one of the API is called by the JOBJECTs -

QLIST retrive_cinemas(QString& options)
{
   return (staticobject::getcinemas(options));
}

or

QLIST retrive_cinemas(QString options)
{
   return (staticobject::getcinemas(options));
}
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
dexterous
  • 6,422
  • 12
  • 51
  • 99
  • I'm assuming it you would want to use reference majority of the time. Unlike java you need to have pointers/addresses. Now pointers vs address is a different story. – Moonhead Feb 07 '14 at 08:38
  • Read this whole chapter, you will understand: http://www.drbio.cornell.edu/pl47/programming/TICPP-2nd-ed-Vol-one-html/Frames.html – iDebD_gh Feb 07 '14 at 08:42
  • If method has `void someMethod(TYPE *parameter)` use a pointer. Your var can be a pointer itself: `TYPE *option1; TYPE option2;`. If both, the parameter and the option are pointers (have the asterisk), just use `someMethod(option1);` Otherwise use `someMethod(&option2);` – Sebastian Lange Feb 07 '14 at 09:00
  • Pass by reference works on the same object while pass by value copies the whole object. Since the native QT objects can be quite big, a pass by reference is way faster. If you do not modify the object use a const reference. http://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value#373429 – Sebastian Lange Feb 07 '14 at 09:09
  • In this particular example, it would make no difference as `QString` uses copy-on-write (implicit sharing) semantics. – cmannett85 Feb 07 '14 at 09:13
  • @cmannett85 That's not true. Passing any of the implicitly shared classes by value **always** costs you an equivalent of a cache line synchronization between all cores due to the atomic increment of the reference count. It has a measurable impact on performance of *all cores*, if you do it often. So it's a rather bad thing to do unnecessarily. If what you said was true, then Qt code itself would not be passing all those implicitly shared objects by const reference! – Kuba hasn't forgotten Monica Feb 07 '14 at 16:27

3 Answers3

4

It seems to me that your problem can be reduced to something like this:

You have a function/method f(), and a class X, and you want to know if/when X should be passed to f() by reference or not.

You can identify three options:

void f(X v)            // #1 - pass by value
void f(const X& cr)    // #2 - pass by const reference (&)
void f(X& r)           // #3 - pass by reference (&)
  1. If X is cheap to copy (e.g. it's an int, a double, etc.), and you do not want to modify it, then pass by value (#1).

  2. If X is not cheap to copy (e.g. it's a vector, a string, etc.), and yo do not want to modify it, then pass by const reference (#2).

  3. If you want to modify the argument of type X inside f(), then pass by reference.


In the particular code you posted, since QString is a full-fledged class which is not cheap to copy as e.g. an int or a double (even if it uses COW techniques and "implicit sharing", I believe that copying still implies a call to something like Win32 InterlockedIncrement() for increasing the ref count in a thread-safe atomic way), I'd pass it by const reference (i.e. const QString &, #2) if you do not want to modify it inside the function.
Just pass by reference (QString&, #3) if you want to modify it inside the function's body.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • I think your edit is a little misleading. `QString` is still very cheap to copy, even if not as cheap as passing by reference. Reference counting uses atomic increment and decrement which is faster than thread safety achieved by using locks. So it is really not all that "bad". – dtech Feb 07 '14 at 09:38
  • @ddriver: As you wrote, ref counting (which requires atomic increment/decrement) is slower than a simple passing by (const) reference. I don't like _premature pessimizations_ :) in C++ code. – Mr.C64 Feb 07 '14 at 09:56
  • When you say `not cheap" you imply "expensive", which it isn't. It would be to deep-copy, but implicit sharing IS STILL CHEAP, although not as cheap passing by reference. I cannot guarantee to what you meant to say, I am just telling you what you sound like you are implying, as a 3rd person reading your comment. – dtech Feb 07 '14 at 10:11
  • For me, an `int` or a `double` are _cheap_ to copy. Something that involves a call to an interlocked atomic operation (like Win32 `InterlockedIncrement()`) is _not_ cheap to copy, or at least is more expensive than a simple _pass by_ `const &`. – Mr.C64 Feb 07 '14 at 10:17
  • I hope you do know atomic operations take a single clock cycle, which ... is kind of the reason they are called atomic. "Less cheap" does not mean "not cheap" – dtech Feb 07 '14 at 10:20
  • @ddriver: I hope you do know that [COW is a pessimization](http://stackoverflow.com/a/1649046/1629821) [in multi-threading](http://www.gotw.ca/publications/optimizations.htm), and in modern C++11 `std::string` is banned from using COW. – Mr.C64 Feb 07 '14 at 10:38
  • @Mr.C64 COW done using modern locked atomic increments/decrements for reference counting is not as bad as your reference would lead us to suggest, but it is still a pessimization, and that's why Qt itself passes all non-numeric, non-pointer input arguments as const references. – Kuba hasn't forgotten Monica Feb 07 '14 at 16:30
  • @KubaOber: Thanks. I agree with the Qt style you cited of passing those types of arguments by const references. I'm against "premature pessimizations" :) – Mr.C64 Feb 07 '14 at 17:17
3

In Qt the answer depends on whether the object you would like to pass uses implicit sharing or not:

Many C++ classes in Qt use implicit data sharing to maximize resource usage and minimize copying. Implicitly shared classes are both safe and efficient when passed as arguments, because only a pointer to the data is passed around, and the data is copied only if and when a function writes to it, i.e., copy-on-write.

You can but you need not pass objects using implicit sharing by reference. They are designed to be passed by value efficiently!

Here you can find the complete explanation and the list of classes using implicit sharing. QString uses implicit sharing.

Bill
  • 11,595
  • 6
  • 44
  • 52
  • 4
    Passing implicitly shared object by value is still less efficient than passing by reference. If you do it a lot, say in a tight loop, there will be tangible difference in performance. – dtech Feb 07 '14 at 09:20
  • 1
    Passing as const ref. is still idiomatic also in Qt, when passing a method argument. For return values, omit the const reference (that also gives more implementation freedom - otherwise you have to return a class member!) – Frank Osterfeld Feb 07 '14 at 12:53
0

In Qt strings are implicitly shared and automatically copied on edit, so they are safe to pass even by value. It is still good practice to pass by reference though (in case it is not a QString), and it is even a tiny bit more efficient, since less data is copied, one memory address vs memory address, size and reference counting.

Generally speaking, it is a good idea to pass by reference when you want to modify the actual object inside the function (note that if you pass implicitly shared QString by value and modify it inside the function, this will not modify the original string but copy it and the changes will be lost after the function returns(unless you return the new string of course)), using references is a little more convenient than using pointers, and a little safer too. Also, if the object is larger than a primitive, or the object cannot/should not be copied, you can pass as reference. If you don't want to modify the source object, just make the reference const.

dtech
  • 47,916
  • 17
  • 112
  • 190