I give the following examples to illustrate my question:
class BigClass
{
public:
static int destruct_num;
friend BigClass operator + (const BigClass &obj1, const BigClass &obj2);
std::vector<int> abc;
BigClass()
{
}
~BigClass()
{
destruct_num++;
std::cout << "BigClass destructor " <<destruct_num<< std::endl;
}
BigClass(BigClass&& bc) :abc(std::move(bc.abc))
{
std::cout << "move operation is involed" << std::endl;
}
};
int BigClass::destruct_num = 0;
BigClass operator + (const BigClass &obj1, const BigClass &obj2)
{
BigClass temp;
temp.abc = obj1.abc;
temp.abc.insert(temp.abc.end(), obj2.abc.begin(), obj2.abc.end());
return temp;
}
int main(void)
{
BigClass a;
a.abc = { 1,2,3 };
BigClass b;
b.abc = { 5,6,7 };
BigClass c = a + b;
// for (auto& v : c.abc)
// std::cout << v << " ";
return 0;
}
One problem with regard to operator +
is that we have to generate a temp BigClass
object temporally and then return it. Are there someways to reduce this burden?
Generally:
[...] the compilers are permitted, but not required to omit the copy [...]
Anyway your function should be optimised by any modern compiler because copy elision.
Here an example:
The result of the operator+
is in the assembly section:
call operator+(BigClass const&, BigClass const&)
addl $12, %esp
As you can see, no copy constructor is invoked in order to copy the result.
Indeed, if we disable the copy elision optimisation in GCC, the result changes:
call operator+(BigClass const&, BigClass const&)
addl $12, %esp
subl $8, %esp
leal -20(%ebp), %eax
pushl %eax
leal -56(%ebp), %eax
pushl %eax
call BigClass::BigClass(BigClass&&)
addl $16, %esp
subl $12, %esp
leal -20(%ebp), %eax
pushl %eax
call BigClass::~BigClass()
addl $16, %esp
After the call of operator+
the copy (or move in this case) constructor is called, and after the destructor of the temporary object.
Note that the copy elision is obtained even disabling optimisations (-O0
).
The same result is obtained with an older version: GCC 4.4.7.
Since copy elision is not guaranteed for all architectures, you might implement some different solutions.
One possible solution is to avoid the allocation of a temporary variable inside the function, demanding the caller the reservation of that space.
In order to do that, you should use a "custom" method and avoid to overload the operator+
.
void sum_bigClasses(const BigClass& obj1, const BigClass& obj2, BigClass& output) {
// output.resize(obj1.size() + obj2.size());
// std::copy(...);
}
Another solution it could be to implement a non-const operator for sum. An example:
BigClass& operator+=(const BigClass& rhs) {
// std::copy(rhs.cbegin(), rsh.cend(), std::back_inserter(abc));
return *this;
}
In this way the class interface allows different strategies:
Here, the two examples.
The first point:
BigClass o1;
BigClass o2;
// Fill o1 and o2;
o1 += o2;
// only 2 object are alive
The second point:
BigClass o1;
BigClass o2;
// Fill o1 and o2;
BigClass o3 = o1; // Costructor from o1
o3 += o2;
// three different object
EDIT: Since the function it's a NRVO (the returned expression is not a prvalue) neither the new standard C++17 will guarantee the copy elision.
If you run your code than you see that only 3 destructors are called. That means that the value of tmp object is moved, not copied, because of RVO (Return Value Optimization). The compiler doesn't copy it, because it sees that's not necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With