странные проблемы конструктора C ++ / конструктора копирования в g ++ - PullRequest
1 голос
/ 27 ноября 2009
#include <iostream>
using namespace std;

class X {
        public:
                X() {
                        cout<<"Cons"<<endl;
                }
                X(const X& x){
                        cout<<"Copy"<<endl;
                }
                void operator=(const X& x){
                        cout<<"Assignment called";
                }
};

X& fun() {
        X s;
        return s;
}

int main(){
        X s = fun();
        return 0;
}

Этот код также вызывает конструктор копирования. Почему это работает? Я помню, что когда я впервые запустил эту программу, она перестала работать. Но через некоторое время он стал называть эту копию минусами. и сейчас работает !! Wierd.

Но если я заменю, fun () выглядит следующим образом:

X fun() {
        X s;
        return s;
}

Тогда скопируйте минусы. не называется. Я думал, что копия минусы. будет называться в этом случае. Но, как отмечает @ flyfishr64, RVO вступает в игру. Но это все еще не объясняет случай, когда я возвращаю ссылку. Я думаю, что это всегда должно быть segfault.

Есть объяснения?

Ответы [ 4 ]

4 голосов
/ 27 ноября 2009

Возвращает ссылку на объект в стеке, который не существует после возврата из метода - стек свободен, память все еще там, но вы не должны ее использовать

X& fun() {
        X s;
        return s;
}

Когда вы меняете это на:

X fun() {
        X s;
        return s;
}

Вы сейчас возвращаете копию. Если компилятор достаточно умен, он может сделать:

X fun() {
    return X();
}

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

Если это ошибка или нет, зависит от того, обращаетесь ли вы к недействительной памяти.

В вашем примере у вас нет доступа к значениям из структуры. Чтобы увидеть segfault, сначала сохраните ссылку, которую вы вернули с помощью fun(), добавьте некоторые переменные в структуру X и после возврата из fun() вызовите другой метод, который внутренне выделяет часть памяти в стеке (это должно перезаписать исходную память, используемую X в fun) и хранит некоторые значения в стеке (предпочтительно 0). После возврата этого второго метода попытайтесь распечатать значения из X, используя исходную ссылку, возвращенную из fun ...

2 голосов
/ 27 ноября 2009

Чтобы расширить ответ @ flyfishr64

Здесь вызывается конструктор копирования, потому что это:

X s = fun();

- это инициализация. Вы используете fun () для создания объекта, не вызывая конструктор по умолчанию. Это эквивалентно:

X s(fun());

Выводы "Минусы", которые вы видите, относятся к примеру в fun (). Смотрите эту статью: Оператор присваивания в C ++ для более.

2 голосов
/ 27 ноября 2009

В этом коде:

X fun() {
        X s;
        return s;
}

конструктор копирования не вызывается из-за оптимизации возвращаемого значения, которая позволяет компилятору обходить создание локальной переменной 's' и создавать X непосредственно в возвращаемой переменной.

Вы можете узнать больше о RVO здесь

0 голосов
/ 28 января 2010

Когда вы возвращаете ссылку на локальную переменную, подобную этой, вы вызываете неопределенное поведение.

В данном случае это работает, потому что ни одна из функций class X фактически не использует указатель this, поэтому не имеет значения, что он больше не действителен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...