Почему конструктор копирования не вызывается в этом коде - PullRequest
0 голосов
/ 15 октября 2011

Так почему конструктор Copy не вызывается в функции " const Integer + + (const Integer & rv) ". Это из-за RVO. Если да, что мне нужно сделать, чтобы предотвратить это?

#include <iostream>

using namespace std;

class Integer {
    int i;

public:
    Integer(int ii = 0) : i(ii) {
        cout << "Integer()" << endl;
    }

    Integer(const Integer &I) {
        cout << "Integer(const Integer &)" << endl;
    }

    ~Integer() {
        cout << "~Integer()" << endl;
    }

    const Integer operator+(const Integer &rv) const {
        cout << "operator+" << endl;
        Integer I(i + rv.i);
        I.print();
        return I;
    }

    Integer &operator+=(const Integer &rv) {
        cout << "operator+=" << endl;
        i + rv.i;
        return *this;
    }

    void print() {
        cout << "i: " << i << endl;
    }
};

int main() {
    cout << "built-in tpes:" << endl;
    int i = 1, j = 2, k = 3;
    k += i + j;
    cout << "user-defined types:" << endl;
    Integer ii(1), jj(2), kk(3);
    kk += ii + jj;

}

Я получаю ошибку, если закомментирую конструктор копирования. Я ожидаю, что конструктор копирования будет вызван, когда оператор + вернется. Ниже приводится вывод программы

built-in tpes:
user-defined types:
Integer()
Integer()
Integer()
operator+
Integer()
i: 3 // EXPECTING Copy Constructor to be called after this
operator+=
~Integer()
~Integer()
~Integer()
~Integer()

Ответы [ 2 ]

5 голосов
/ 15 октября 2011

Это из-за РВО.Если да, что мне нужно сделать, чтобы предотвратить это?

Да.Но он не был вызван из-за Оптимизации возвращаемого значения компилятором.

Если вы используете GCC, то используйте опцию -fno-elide-constructors, чтобы избежать этого.

Руководство по GCC 4.6.1 говорит,

-fno-elide-constructors

Стандарт C ++ позволяет реализации не создаватьвременный объект, который используется только для инициализации другого объекта того же типа.Указание этой опции отключает эту оптимизацию и заставляет G ++ вызывать конструктор копирования во всех случаях.

1 голос
/ 16 октября 2011

(N) RVO является одним из самых простых для реализации оптимизаций.В большинстве соглашений о вызовах для возврата по значению вызывающая сторона резервирует пространство для возвращаемого объекта и затем передает скрытый указатель на функцию.Затем функция создает объект по указанному адресу.То есть kk += ii + jj; переводится в нечто вроде:

Integer __tmp;

//                  __rtn  this  arg
Integer::operator+( &tmp,  &ii,  jj );

kk += __tmp;

Функция (в данном случае Integer::operator+ принимает первый скрытый аргумент __rtn, который является указателем на неинициализированный блок памяти sizeof(Integer) байт, где должен быть построен объект, второй скрытый аргумент this, а затем аргумент функции в коде.

Затем реализация функции переводится в:

Integer::operator+( Integer* __rtn, Integer const * this, const Integer &rv) {
    cout << "operator+" << endl;
    new (__rtn) Integer(i + rv.i);
    __rtn->print();
}

Поскольку соглашение о вызовах передает указатель, функции не нужно резервировать дополнительное пространство для локального целого числа, которое затем будет скопировано, поскольку она может просто встроить I в вашем коде прямо в полученный указательи избегайте копирования.

Обратите внимание, что не во всех случаях компилятор может выполнять NRVO, в частности, если у вас есть два локальных объекта в функции и вы возвращаете любой из них в зависимости от условия, которое не является выводимым изкод (скажем, значение аргумента функции). Хотя вы могли бы сделать это, чтобы избежать RVO, на самом делечто это сделает ваш код более сложным, менее эффективным и сложным в обслуживании.

...