Уменьшить количество копий вызовов конструктора - PullRequest
0 голосов
/ 27 августа 2018

Следующий код является минимальным примером из проекта, над которым я работаю. Основной вопрос заключается в том, что я хочу сократить количество обращений к конструктору копирования, но мне не ясен правильный способ сделать это.

#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.

  • Два конструктора копирования вызываются для первого аргумента в операторе + и возврата для оператора +.

  • Назначение присваивает результат от присвоения a + b к c.

Основной вопрос: В моем приложении конструктор копирования является дорогостоящим (он включает в себя выделение памяти). Назначение, с другой стороны, относительно дешево. Как правильно уменьшить количество обращений к конструктору копирования?

Я рассмотрел несколько решений, но ни одно не делает меня счастливым:

  • Как я понимаю, после прочтения оператор + не должен иметь ссылку на первый аргумент, поскольку это помогает с цепочкой временных значений. Поэтому этот конструктор копирования кажется неизбежным.

  • Следующий код значительно быстрее (из-за отсутствия вызовов конструктора копирования): c = a; c += b; Я мог бы написать свой код, используя этот формат, но для этого требуется более тонкий подход. Я бы предпочел, чтобы компилятор был умнее, чем я сам, чтобы сделать эти настройки.

  • Я мог бы реализовать функцию add(MyClass &,const MyClass &,const MyClass &);, но это теряет простоту использования оператора сложения (и требует большого (бессмысленного) кодирования из-за количества различных типов данных, которые я использую).

  • Я посмотрел на вопросы, но не вижу предложений, которые могли бы улучшить производительность в этом случае:

Конструктор копирования вызывается дважды , Конструктор копирования вызывается дважды и Условия для исключения копирования

Ответы на комментарии:

  • Личные данные включают MPFR и MPFI, а конструктор включает инициализацию этих данных. Возможно, подойдет другая реализация конструкторов, но я не уверен.

  • Я рассматривал конструктор перемещения, но бывают случаи, когда мне также требуется назначение копирования. Из cppreference видно, что они не могут сосуществовать (или, по крайней мере, произошла ошибка при первой попытке). Похоже, что это должен быть лучший вариант.

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

В других случаях, чтобы свести к минимуму вызовы конструктора копирования, я рекомендую вам определить конструктор перемещения и полностью перенаправить аргументы вашего оператора. Переместить конструктор:

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

Идеальный оператор переадресации:

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

При правильных вызовах ваш оператор будет использовать конструктор перемещения вместо конструктора копирования. Например, если вы добавляете объекты, которые выходят из функции, и сразу же сохраняете результат (например, MyClass c=a+b; вместо MyClass c;c=a+b;), благодаря RVO вы можете сохранить конструктор копирования.

Допустим, у вас есть функция, которая возвращает экземпляр MyClass:

MyClass something() {return MyClass();}

Если вы добавите в функцию возвращаемые значения и сохраните их немедленно, например ::

MyClass c=something()+something();

Тогда конструктор копирования не будет задействован.

Я поместил ряд примеров здесь , где я использовал параметр const MyClass& с operator+ и параметры совершенной пересылки с operator-. Вы можете видеть, что это имеет значение в последнем примере, но не во всех остальных. Вот почему я сказал «с правильными звонками». Если вам приходится манипулировать объектами, которые можно пересылать подобным образом, это может стоить выстрела.

0 голосов
/ 27 августа 2018

Вы передаете lhs копией. Вот почему у вас есть дополнительный вызов конструктора копирования. Измените ваш operator+:

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

...