减少复制构造函数调用

时间:2018-08-27 16:28:28

标签: c++ copy-constructor rvo

以下代码是我正在处理的项目中的最小示例。主要问题是我想减少对copy构造函数的调用,但是对我而言,尚不清楚正确的方法。

#include<iostream>

class MyClass
{
public:
    MyClass() {std::cout << "Default Constructor\n";}
    MyClass(const MyClass &input) {std::cout << "Copy Constructor\n";}
    MyClass & operator=(const MyClass &input) 
         {std::cout << "Assignment\n"; return *this;}
    MyClass & operator+=(const MyClass &input) {return *this;}
    friend MyClass operator+(MyClass lhs,const MyClass &);
};

MyClass operator+(MyClass lhs,const MyClass &rhs) 
    {lhs+=rhs;return lhs;}

int main()
{
    MyClass a,b,c;
    c=a+b;
    return 0;
}

运行代码时,输​​出为:

Default Constructor
Default Constructor
Default Constructor
Copy Constructor
Copy Constructor
Assignment
  • 在构造a,b和c时会调用三个默认构造函数。

  • 在operator +中的第一个参数调用了两个副本构造函数,在operator +中返回了两个副本构造器。

  • 该赋值将赋值a + b的结果赋给c。

主要问题:在我的应用程序中,复制构造函数非常昂贵(涉及内存分配)。另一方面,分配相对便宜。减少对复制构造函数的调用的正确方法是什么?

我考虑了一些解决方案,但没有一个能让我高兴的:

  • 据我了解,从阅读中可以看出,operator +不应为第一个参数提供引用,因为这有助于链接临时变量。因此,此复制构造函数似乎不可避免。

  • 以下代码明显更快(由于没有复制构造函数调用):c = a; c += b;我可以使用这种格式编写代码,但这需要更精致的方法。我希望编译器比我自己做这些调整更聪明。

  • 我可以实现函数add(MyClass &,const MyClass &,const MyClass &);,但这使使用加法运算符变得容易(由于我使用的不同数据类型的数量,因此需要很多(无意识的)编码)

  • 我已经查看了问题,但是在这种情况下,我看不到任何可以提高性能的建议:

Copy constructor called twiceCopy constructor called twiceConditions for copy elision

回复评论:

  • 私有数据包括MPFR和MPFI,构造函数包括此数据的初始化。也许可以使用不同的构造函数实现,但是我不确定。

  • 我考虑过一个move构造函数,但是有时候我也想要一个副本副本分配。从cppreference看来,它们无法共存(或者至少当我初次尝试时出现错误)。看来这应该是最好的选择。

2 个答案:

答案 0 :(得分:1)

您正在通过副本传递lhs。这就是为什么您需要额外的副本构造函数调用的原因。修改您的operator+

MyClass operator+(const MyClass &lhs, const MyClass &rhs)

答案 1 :(得分:1)

在其他方面,为了最大程度地减少复制构造函数的调用,建议您定义move构造函数,并完善转发操作符参数。移动构造函数:

MyClass(MyClass &&input) {std::cout << "Move Constructor\n";}

完美转发运算符:

template<typename T>
    friend MyClass operator+(T &&lhs,T &&rhs) {return std::forward<T>(lhs);}

通过正确的调用,您的运算符将涉及move构造函数而不是copy构造函数。例如,如果您添加来自函数的对象并立即存储结果(例如,MyClass c=a+b;而不是MyClass c;c=a+b;),那么多亏RVO,您可以保存副本构造函数。

假设您有一个返回MyClass实例的函数:

MyClass something() {return MyClass();}

如果添加函数返回值并立即存储它们,例如:

MyClass c=something()+something();

那么就不会涉及复制构造函数。

我举了一系列示例here,其中我将const MyClass&参数与operator+一起使用,并将完善转发参数与operator-一起使用。您可以看到在上一个示例中有所不同,但在其他所有示例中却没有。这就是为什么我说“通过正确的电话”。如果您必须操纵可以转发的对象,那么值得一试。