2016-05-20 2 views
2

Рассмотрим следующий сценарийПочему конструктор копирования не вызывается, когда возвращаемое значение из перегруженного оператора передается

class Integer 
{ 
    long long n; 
    public: 
    Integer(long long i):n(i){} 
    Integer(){cout<<"constructor";} 
    void print() 
    { 
     cout<<n<<endl; 
    } 
    Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;} 
    Integer operator+(Integer b); 
}; 

Integer Integer:: operator+(Integer b) 
{ 
    this->n = this->n + b.n; 
    return *this; 
} 
int main() 
{ 
    // your code goes here 
    Integer a(5); 
    Integer b(6); 
    Integer c(a+b);//line 1 
    return 0; 
} 

Если a+b был временным, то я понимаю, что конструктор копирования не будет называться. Но a+b не возвращает временный. Выход я получаю

copy constructor 6 //this is because Integer object is passed as value to operator+ 
copy constructor -5232903157125162015 //this is when object is returned by value from operator+ 

Я думаю, что должен быть еще один вызов, когда a+b используется для инициализации c. Большинство связанных вопросов связаны с оптимизацией возвращаемых значений, но i не может связывать RVO с этим.

+0

Unrelated; Не следует писать 'b.n + = this-> n; return b; '? –

+3

Отключить тему: 'a + b' как бы разбит. Он модифицирует 'a' способ, которым будет работать оператор + =. – user4581301

+0

'a + b' * * * возвращает временный (поскольку' operator + 'возвращает значение), а вызов удаляется * copy elision *. –

ответ

1

Вы являетесь свидетелем конструктора copy ellission.

Когда безымянного временный характер, не связан с каких-либо ссылок, будут перемещены или скопированы в объект одного и того же типа (без учета верхнего уровня CV-квалификацию), копия/перемещение опускается. Когда это временное построено, оно создается непосредственно в хранилище, где оно иначе было бы перемещено или скопировано. Когда неназванным временным является аргумент оператора return, этот вариант экземпляра копии называется RVO, «оптимизация возвращаемого значения».

Для просмотра ожидаемого результата (не eliding конструктора) использовать -fno-elide-constructors опцию в gcc.

EDIT

Как уже упоминалось в моем комментарии на ваш вопрос Integer:: operator+ не совсем верно. Кроме того, как упоминалось в других ответах, ваш конструктор копирования не выполняет инициализацию члена и, следовательно, вызывает неопределенное поведение.

+0

Это не причина ошибки. – Caduchon

+0

Собственно, он отвечает на часть вопроса. Пожалуйста, отредактируйте свой ответ, затем я смогу удалить свой нижний план. – Caduchon

+0

@Caduchon Вопрос не в ошибке. Кажется, что OP думает, что ошибка - это ОК, чтобы проиллюстрировать точку. – juanchopanza

1

Чтобы ответить на ваш вопрос, конструктор копирования никогда ничего не копирует, а оператор + не перегружен правильно. Кроме того, конструктор по умолчанию оставляет класс Integer неинициализированным членом, который может вызывать неопределенные поведения (не 100% на этом), что может вызвать некоторые проблемы.

Так что же происходит, потому что копия конструкторы никогда не скопировать значение в n члена *this, называется конструктором по умолчанию, и станд :: соиЬ отображает все, что на месте n из «a.n» ; который, если неинициализирован, может быть чем угодно.

This website от Caltech это хорошо, если оно несовершенно, ссылка для операторов перегрузки.

Попробуйте это.

#include <iostream> 

using namespace std; 


class Integer{ 
    long long n; 
public: 
    Integer(long long i):n(i){} 
    //Integer(){cout<<"cosntructor";} //This creates an unitialized instance of integer. 

    Integer(){// Do this instead. 
     n = 0; 
     cout << "Constructor" << endl; 
    } 

    void print(){ 
     cout<<n<<endl; 
    } 
    //Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;} // This isn't a copy contructor. 

    Integer(const Integer &a){ //This is a copy contructor. 
     this->n = a.n; 
     cout << "Copy constructor" << " " << a.n << endl; 
    } 

    Integer& operator+=(const Integer &); 
    Integer operator+(const Integer &); 
}; 

Integer& Integer::operator+=(const Integer &b){ //Always overload incrementors/decrementors first, makes life easier. 
    this->n = this->n + b.n; 
    return *this; 
} 


Integer Integer:: operator+(const Integer &b){ 
    return Integer(*this)+=b.n; //Notice the use of overloaded incrementor inside the '+' operator. 
} 

int main(){ 
    // your code goes here 
    Integer a(5); 
    Integer b(6); 
    Integer c(a+b);//line 1 
    return 0; 
} 
+0

Непонятно, как вы отвечаете на вопрос. Это может потребовать некоторого уточнения. – juanchopanza

+0

'operator +' было бы лучше, если бы вы перевернули 'this' и' b', потому что вы изменяете 'this':' return b.n + = this-> n; ' – user4581301

+0

@juanchopanza Хорошо, я добавил некоторые разъяснения. – NonCreature0714

0

Он имеет правильное количество копий CTOR звонков:

первый для b, потому что operator+(Integer b) принимает Integer по значению.

2nd is for c создатель: operator+ результат.

По стандарту может быть 3-й вызов: return *this, возможно, создал временное, которое позже будет скопировано до c. Но на самом деле это всегда устранено. Вы можете отключить эту элицию в gcc & clang, и вы не можете отключить ее в MSVC.

2

Вы говорите, что a+b не возвращает временное. Неправильно. a+b (даже если он меняет a, который, конечно же, вводит в заблуждение будущего читателя ...) возвращает временную копию a, потому что объявлено, что она возвращает Integer, а не ссылку (Integer&).

Так вот что происходит:

Integer a(5); // creates an integer from int 5 
Integer b(6); // creates an integer from int 6 

Integer c(a+b); /* first creates a temp using copy ctor from b 
        updates a 
        creates a temp copy of the result 
        should create an integer by copy of the result (elided) 
        destroys the temporary created from b 
*/ 

return 0; // destroys c, b, a 

BTW: Вы забыли инициализировать n в вашей копии CTOR - это должно быть:

Integer(const Integer &a):n(a.n) {cout<<"copy constructor"<<" "<<a.n<<endl;} 
Смежные вопросы