2012-04-10 1 views
0

Я следующий простой код с классом, включая обычный конструктор и конструктор копированияC++ амперсанд принимающий объект из функции

class largeObj 
{ 
public: 
    largeObj() 
    { 
     printf("\nNormal constructor\n"); 
    } 

    largeObj(const largeObj& mv) 
    { 
     printf("\nCopy constructor\n"); 
    } 

    ~largeObj() 
    { 
     printf("\nDestroying..\n"); 
    } 

    void tryme() 
    { 
     printf("\nHi :)\n"); 
    } 

}; 

largeObj iReturnLargeObjects() 
{ 
    largeObj md; 


    return md; 
} 


int main() 
{ 

    largeObj mdd = iReturnLargeObjects(); 

    mdd.tryme(); 

    return 0; 
} 

Выход

Обычный конструктор

Copy конструктор

Уничтожение ..

hi :)

и я понял почему.

Но если я заменяю следующую строку

largeObj mdd = iReturnLargeObjects(); 

с

largeObj& mdd = iReturnLargeObjects(); 

Выход одно и то же, почему?

Я имею в виду: не должна ли быть другая копия в первом случае (без &)? В чем разница между этими двумя строками и почему они ведут себя одинаково?

+0

Второй не должен компилироваться. Это связывает временную ссылку на неконстантную. – jrok

+0

Я немного отлаживал, и я узнал, что обычный конструктор вызывается объектом md в iReturnLargeObjects(), тогда конструктор копирования вызывается при возврате md; и затем деструктор вызывается на md-объекте непосредственно перед возвратом из iReturnLargeObjects(). Так что, предположительно, в памяти создается еще одна большая Obj. Возникает вопрос: почему объект largeObj mdd = не создает другую переменную копирования? Это из-за RVO? И поэтому все здесь согласны с тем, что второй объект largeObj & mdd = полностью незаконен и скомпилирован из-за чего-то странного – paulAl

+0

@ jrok, он все равно должен компилироваться. Если вы, например, создаете ссылку в стеке, эта ссылка не рассматривается как tempory, потому что компилятор не проверяет, откуда он пришел, только то, что оно является действительной ссылкой, поэтому, когда оно возвращается, оно возвращается как ссылка. Это работает ТОЛЬКО, потому что это конструктор копирования. – 8bitwide

ответ

4
largeObj& mdd = iReturnLargeObjects(); 

Вы не можете привязать переменную lvalue к rvalue. Это незаконный C++ и допускается только некоторыми расширениями компилятора. Однако семантика вопроса остается неизменной, даже если эта ссылка была const, и поэтому назначение юридическое.

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

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

+1

Здесь нет RVO - если бы не было, вы бы не увидели сообщения 'Copy contructor' и' Destorying' перед 'Hi' –

+0

Спасибо, а как насчет возвращаемых типов функций? Я имею в виду: largeObj & myFunction(); возвращает ссылки вместо простого largeObj лучше или благодаря RVO, я могу забыть об этих вещах и просто вернуть largeObj? – paulAl

+0

@ChrisDodd, теперь я немного смущен. – paulAl

0

Какой компилятор вы использовали, я готов поспорить, что это сработало, потому что любой используемый вами компилятор не обнулял неиспользованные переменные стека после возврата, вы не получили ошибку seg, потому что все еще ваш стек. в основном md остался в стеке, который вам по-прежнему разрешен. Разница между ними равна

большоеObj md;

- это переменная, получающая переменную целиком.

largeObj & mdd = iReturnLargeObjects();

- ссылка на переменную, которая из-за ленивого компилятора все еще существует.

+0

Я использовал визуальную студию 2010 – paulAl

+0

Теперь это странно, Visual Studio обычно очень хороша в том, чтобы делать все, что не входит в стандарт, но заставляет кодеров совершать глупые ошибки , В основном это работает, потому что конструктор копирования возвращает ссылку по определению. И стек не очищается. Добавьте эту функцию до вызова mdd.tryme(), чтобы узнать, что я имею в виду. int messupstack() {char * a = "lllllllllllllllllllll '"; return 5; } – 8bitwide

+0

Подождите, я отставлен, этот последний раздел ничего не сделает, потому что tryme просто распечатывает данные не в экземпляре класса. Вам понадобятся переменные класса, чтобы увидеть неверные результаты. – 8bitwide

Смежные вопросы