Reputation: 2863
By value:
class Test {
private:
HugeObject h; // Copy 1
public:
void SetObject(HugeObject hugeObject) { // Copy 2
h = hugeObject; // Copy 3
}
}
// Somewhere else
Test t();
t.SetObject(HugeObject());
This is bad because three implicit copies are created. If I change the parameter of SetObject()
to const HugeObject& hugeObject
, it's also bad because I'd be storing something that will no longer exist outside that function's scope.
So, to prevent copying twice and storing invalid data, I can copy just twice instead:
void SetObject(const HugeObject& hugeObject) {
h = HugeObject(hugeObject); // Copy constructor
}
Is this a valid way of handling this situation or am I misunderstanding something? A silly optimization or not an optimization at all? Are there better ways, aside from storing a pointer?
Upvotes: 1
Views: 1888
Reputation: 348
When you declare h
first in the class, what you're actually doing is calling the object's default constructor, which is implicitly called upon creation of an object that doesn't use an explicit syntax; that is,
HugeObject h; // Calls default constructor implicitly.
HugeObject h2 = HugeObject(); // Still calls default constructor.
HugeObject ougeHobject = HugeObject(h); // Calls copy constructor.
h2 = ougeHobject; // calls HugeObject's operator=
The last part is what I'm really getting at. When you assign h
to hugeObject
in SetObject
, you're actually using HugeObject
's operator=
. This is not the same thing as construction. If operator=
isn't defined by a class, then the C++ implementation synthesizes it; this default works by assigning each member of the right-hand-side object of a basic type (double
, for instance,) to the corresponding member on the left-hand-side object. For class types stored as variables of hugeObject,
their corresponding operator=
s are called.
To answer the question directly now, if you'd like to limit the copies you make, just assign h to a reference of hugeObject
in SetObject
, as both have already been instantiated:
void SetObject(const HugeObject& hugeObject) {
h = hugeObject;
}
Although technically out of scope, the const HugeObject&
's values are copied via HugeObject
's operator=
to h
. This does not implicitly construct a new object, as both have already been instantiated; again, it just calls operator=
.
Upvotes: 2
Reputation: 180980
Why not get the object during construction? Then you get the original creation and a copy.
#include <iostream>
using std::cout;
class Huge
{
public:
Huge() { cout << "constructor\n"; }
Huge(const Huge & h) { cout << "copy\n"; }
Huge & operator = (const Huge & h) { cout << "assignment\n"; return *this; }
};
class Holder
{
Huge h;
public:
Holder(const Huge & h_) : h(h_) {};
};
int main()
{
Huge bar;
Holder foo(bar);
}
Output:
constructor
copy
Upvotes: 1
Reputation: 303576
This is bad because three implicit copies are created.
Actually, only one copy happens in that situation. The HugeObject()
temporary is constructed in-place as the argument hugeObject
, and then it is copied into h
from there.
If I change the parameter of SetObject() to
const HugeObject& hugeObject
, it's also bad because I'd be storing something that will no longer exist outside that function's scope.
That's not accurate either - you're still copying into h
, you're not storing a reference to it. That would save you a copy in the case of:
HugeObject h;
t.SetObject(h); // copy from h into t.h, no copy necessary
// into the argument of SetObject()
Taking hugeObject
by const&
is the best way to do it in C++03. If you have access to a C++11 compiler, and your HugeObject
is efficiently movable, you should additionally add an rvalue overload:
void setObject(HugeObject&& hugeObject) {
h = std::move(hugeObject);
}
Upvotes: 2