如何在C++中减少自定义类的+运算符内存消耗?

fee*_*ree 6 c++ c++11

我举几个例子来说明我的问题:

 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时间上生成临时对象然后返回它.有没有办法减轻这种负担?

Bia*_*sta 7

通常:

[...]编译器是允许的,但不要求省略副本[...]

无论如何,你的函数应该由任何现代编译器优化,因为复制省略.

是一个例子:

结果部分的结果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)

通过这种方式,类接口允许不同的策略:

  • 如果您不需要保留所有不同的状态,请避免分配3个不同的对象,但只能分配2个.
  • 允许分配3个不同的对象,避免在运营商内部进行临时构建.

这里有两个例子.

第一点:

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都不能保证复制省略.