我举几个例子来说明我的问题:
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;
}
Run Code Online (Sandbox Code Playgroud)
关于的一个问题operator +是我们必须在BigClass时间上生成临时对象然后返回它.有没有办法减轻这种负担?
通常:
[...]编译器是允许的,但不要求省略副本[...]
无论如何,你的函数应该由任何现代编译器优化,因为复制省略.
这是一个例子:
结果部分的结果operator+是:
call operator+(BigClass const&, BigClass const&)
addl $12, %esp
Run Code Online (Sandbox Code Playgroud)
如您所见,为了复制结果,不会调用任何复制构造函数.
实际上,如果我们在GCC中禁用复制省略优化,结果会发生变化:
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
Run Code Online (Sandbox Code Playgroud)
在调用operator+副本(或在这种情况下移动)之后调用构造函数,并在临时对象的析构函数之后调用.
请注意,即使禁用optimisations(-O0)也可以获得复制省略.
使用旧版本获得相同的结果:GCC 4.4.7.
由于并非所有体系结构都保证了复制省略,因此您可以实现一些不同的解决方案.
一种可能的解决方案是避免在函数内部分配临时变量,要求调用者保留该空间.为了做到这一点,你应该使用"自定义"方法,并避免重载operator+.
void sum_bigClasses(const BigClass& obj1, const BigClass& obj2, BigClass& output) {
// output.resize(obj1.size() + obj2.size());
// std::copy(...);
}
Run Code Online (Sandbox Code Playgroud)
另一种解决方案可能是为sum实现非const运算符.一个例子:
BigClass& operator+=(const BigClass& rhs) {
// std::copy(rhs.cbegin(), rsh.cend(), std::back_inserter(abc));
return *this;
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,类接口允许不同的策略:
这里有两个例子.
第一点:
BigClass o1;
BigClass o2;
// Fill o1 and o2;
o1 += o2;
// only 2 object are alive
Run Code Online (Sandbox Code Playgroud)
第二点:
BigClass o1;
BigClass o2;
// Fill o1 and o2;
BigClass o3 = o1; // Costructor from o1
o3 += o2;
// three different object
Run Code Online (Sandbox Code Playgroud)
编辑:由于函数是NRVO(返回的表达式不是prvalue),新的标准C++ 17都不能保证复制省略.