2

I am programming in C++/Qt. Do these code snippets correctly save the content of a reference by copying?

With Initialization List:

Foo(const Bar &bar) : bar(bar) { }
Bar bar; //member variable of Foo class

In a Setter-Method:

void TestClass::setName(const QString &name) {
    this->name = name;
}
QString name; //member variable of TestClass class

If it does, does this copy the copy?:

void TestClass::setName(const QString name) {
    this->name = name;
}
QString name; //member variable of TestClass class

Does this lead to undefined behavior, because the referenced object might be deconstructed early?:

Foo(Bar &bar) : bar(bar) { }
Bar &bar; //member variable of Foo class

Is it bad style to store the content of a passed reference? What would be a better way to pass arguments that need to be stored?

Luca Fülbier
  • 2,641
  • 1
  • 26
  • 43
  • If the member itself isn't a reference then it will store a copy. As for your last example it depends, accessing the member after whatever it references has been destroyed is indeed undefined behaviour, but that doesn't mean member references are necessarily [bad style](http://stackoverflow.com/questions/12387239/reference-member-variables-as-class-members). – user657267 Jan 16 '15 at 02:07

1 Answers1

5

Copying overhead

Both of these methods will copy data from the input string into the class variable:

QString name; //member variable of TestClass class

void TestClass::setName1(const QString & name) {
    this->name = name;
}

void TestClass::setName2(const QString name) {
    this->name = name;
}

In the second one, a copy of the QString will be made on the stack first. You are correct to think that pass-by-value causes more copy overhead. This is the reason pass-by-value is generally discouraged for classes that contain a lot of data.

See this question for more discussion of pass-by-reference vs. pass-by-value.

However, in this specific example, the difference in overhead is very small: QString uses implicit sharing. When you copy QString (e.g. with operator=), it simply creates another reference to the same data. So just copying a QString — even a very long one — isn't substantively more intensive than copying a reference.

Dangling references

Foo(Bar &bar) : bar(bar) { }
Bar &bar; //member variable of Foo class

This code can, indeed, cause problems if the original bar is deleted while your Foo class is still around (this is known as a "dangling reference"). Using references in this manner is a lot like using pointers: it's up to you as the programmer to make sure all objects involved manage memory correctly.

If you're concerned about dangling pointers/references in your Qt application, try QPointer:

A guarded pointer, QPointer, behaves like a normal C++ pointer T *, except that it is automatically set to 0 when the referenced object is destroyed (unlike normal C++ pointers, which become "dangling pointers" in such cases). T must be a subclass of QObject.

You could rewrite the code above like this:

Foo(Bar * bar) : bar(bar) { }
QPointer<Bar> bar; //member variable of Foo class

Then you are partially protected from dangling pointer errors:

Bar * myBar = new Bar();
Foo myFoo(myBar);  // myFoo->bar is set to myBar
delete myBar;      // myFoo->bar becomes 0

As long as your class methods check this->bar for null before using it, you can avoid operating on uninitialized memory. (Or, if you don't, you'll get a segmentation fault pretty quickly, instead of potentially more subtle undefined behavior from accessing an already-deleted address.)

Community
  • 1
  • 1
Alex P
  • 1,559
  • 11
  • 23
  • I try to avoid using Pointers as parameters, as the reference syntax keeps the code clean and the memory management easier. But I see how it makes sense to use them when transferring ownership. As a C++ beginner it just bugs me, that there is no definite best way of handling situations like this. – Luca Fülbier Jan 16 '15 at 09:55
  • Just another quick question: If in the first example I changed the member "name" to "QString &name;", would it still copy the data, as the reference was already intialized in the constructor and now cannot be reseated? – Luca Fülbier Jan 16 '15 at 10:11