2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2  След.
 
 returning reference to temporary (ворнинг от gcc)
Сообщение20.02.2008, 19:57 
Экс-модератор


17/06/06
5004
При компилировании функции вида
Код:
const Foo& func(int x) {
    return Foo(x,f(x));
}
(где Foo - некоторый класс, а функция func объявлена в процессе описания некоторого глубоко третьего класса в заголовочном файлике, то есть, я так понимаю, является inlineовой) компилятор gcc выдает предупреждение: returning reference to temporary. Функция работает правильно, программа признаков глючности не демонстрирует. И все-таки, каков точный смысл предупреждения?

 Профиль  
                  
 
 
Сообщение20.02.2008, 20:23 


21/03/06
1545
Москва
Ну дык. Вы фактически создаете автоматический объект типа Foo, а потом возвращаете на него ссылку при выходе из функции. Объект-то уже перестает существовать (он автоматический).

Цитата:
где Foo - некоторый класс, а функция func объявлена в процессе описания некоторого глубоко третьего класса в заголовочном файлике, то есть, я так понимаю, является inlineовой

А почему Вы делаете вывод про inline? Вообще-то inline - это рекомендация компилятору, вовсе не подлежащая обязательному выполнению, тем более, ожидать от компилятора создания inline функций по его собственной инициативе - весьма оптимистично.

 Профиль  
                  
 
 
Сообщение20.02.2008, 20:33 
Супермодератор
Аватара пользователя


29/07/05
8248
Москва
Используйте какой-нибудь способ чтобы проверить, вызван ли деструктор для объекта, ссылку на который Вы возвращаете.

 Профиль  
                  
 
 
Сообщение20.02.2008, 20:44 


21/03/06
1545
Москва
Даже если вдруг (по причине того, что компилятор все сделал правильно, и сделал func inline) деструктор не вызывается, это:
1. Не гарантирует правильность работы данного кода в будущем, когда изменятся настройки компилятора/программа.
2. Не решает проблему warning'а. А warning'ов в программе быть не должно. В крайнем случае, если программист точно знает, что он делает, варнинги окружаются директивами препроцессора, которые запрещают вывод варнингов для кода, находящегося внутри них. Но это экзотика, для данного кода вряд ли целесообразная.

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:01 
Модератор
Аватара пользователя


11/01/06
5702
зафиксить эту проблему можно вроде бы (проверять влом) так, например:
Код:
const Foo& func(int x) {
    return *(new Foo(x,f(x)));
}

Хотя в этом случае нужно будет потом явно вызывать деструктор полученного класса (или получить утечку памяти).

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:06 
Экс-модератор


17/06/06
5004
maxal писал(а):
Хотя в этом случае нужно будет потом явно вызывать деструктор полученного класса (или получить утечку памяти).
Неужели так всё коряво? Должен же быть аккуратный предусмотренный способ.?

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:12 
Супермодератор
Аватара пользователя


29/07/05
8248
Москва
Нельзя возвращать ссылки на временные объекты. Это то же самое, что вернуть из функции адрес локальной переменной. Это по сути неверно, даже если вдруг почему-то работает.

Разумных способов три. Первый - создавать объект снаружи и передавать его по ссылке в функцию. Второй - возвращать объект по значению. Третий - с помощью new. Но в третьем случае я бы поостерегся использовать возвращение по ссылке, а возвращал бы адрес. Это нагляднее и напоминает о том, что память придется возвращать.

А как снаружи используется возвращаенная ссылка?

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:31 
Экс-модератор


17/06/06
5004
PAV писал(а):
А как снаружи используется возвращаенная ссылка?
Сразу же подставляется в некоторую другую функцию.

Мне вообще не понятно, как эта функция работает. Ведь она при каждом вызове создает временный объект (где? в стеке или в куче?), а потом его в какой-то момент стирает? Да, вообще, наверное, лучше сделать один глобальный объект, а потом его перезаливать (вызывать конструктор)? Только это сильно не thread-safe.

PAV писал(а):
Это нагляднее и напоминает о том, что память придется возвращать.
У меня утечки памяти не происходит. По крайней мере, в task managerе это не видно, а функция вызывается очень много раз.

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:35 
Супермодератор
Аватара пользователя


29/07/05
8248
Москва
Локальные переменные создаются в стеке. Проверьте все-таки, когда вызывается деструктор. А лучше всего - сделайте деструктор, который заведомо портит объект и проверьте, будет ли и дальше работать.

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

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:40 


21/03/06
1545
Москва
Цитата:
Мне вообще не понятно, как эта функция работает. Ведь она при каждом вызове создает временный объект (где? в стеке или в куче?), а потом его в какой-то момент стирает?

Автоматические объекты создаются в стеке.
Не стирает, а вызывает деструктор объекта, а потом изменяет содержимое указателя стека. В соседней теме подробно обсуждалось, что память объектов в основном не обнуляется после уничтожения объекта, т.е. какое-то время информация по адресу бывшего объекта акутальна, и ее даже можно некоторое время использовать, что ведет к сложнонаходимым ошибкам.
Кстати, проблему можно решить путем создания статического объекта типа Foo в вашей функции, присвоения значения Foo(x, f(x)) этому объекту, и возвращения ссылки на него. Это значительно лучше, чем new, предложенный maxal'ом, хотя, конечно, все зависит от конкретики.

Цитата:
Да, вообще, наверное, лучше сделать один глобальный объект, а потом его перезаливать (вызывать конструктор)?

Лучше static. Глобальные объекты должны быть обоснованны, тут обоснования я не вижу. Вообще, как правило, подобные вашим заморочки можно легко решить, чуть чуть изменив концепции данного участка программы. Способы предложил PAV.

Цитата:
У меня утечки памяти не происходит. По крайней мере, в task managerе это не видно, а функция вызывается очень много раз.
И не должна. Объект уничтожается каждый раз при выходе из функции. А то, что вы его продолжаете использовать - ваша ошибка, которая скорее всего проявится при дальнейшенм развитии программы.

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:49 
Экс-модератор


17/06/06
5004
То есть правильно ли я понимаю, что программа работает правильно лишь потому, что память при удалении не зануляется?

Поэкспериментирую как-нибудь еще по этому поводу. Просто боюсь на месте химичить с деструктором, так как класс Foo не мой, он встроенный в библиотеку Qt. :lol:

e2e4 писал(а):
Кстати, проблему можно решить путем создания статического объекта типа Foo в вашей функции, присвоения значения Foo(x, f(x)) этому объекту, и возвращения ссылки на него.
То есть что-то типа
Код:
const Foo& func(int x) {
    static Foo res;
    res=Foo(x,f(x));
    return res;
}
? (ворнинг исчез). Тогда объясните только, в чем смысл второй строчки? Верно ли, что там создается временный объект типа Foo(), потом копируется в res, а потом сразу умирает? Или просто выполняется код конструктора на res?

 Профиль  
                  
 
 
Сообщение20.02.2008, 21:58 


21/03/06
1545
Москва
Цитата:
То есть правильно ли я понимаю, что программа работает правильно лишь потому, что память при удалении не зануляется?

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

Цитата:
То есть что-то типа
Код:
const Foo& func(int x) {
    static Foo res;
    res=Foo(x,f(x));
    return res;
}

Совершенно верно.

Цитата:
Тогда объясните только, в чем смысл второй строчки? Верно ли, что там создается временный объект типа Foo(), потом копируется в res, а потом сразу умирает?

Верно. Теоретически оптимизатор может сразу создавать объект по адресу res. Сути это не меняет.

 Профиль  
                  
 
 
Сообщение20.02.2008, 23:13 
Экс-модератор


17/06/06
5004
e2e4 писал(а):
Верно. Теоретически оптимизатор может сразу создавать объект по адресу res. Сути это не меняет.
Ну так это лишнее - создавать второй объект, когда нужен один. Как записать, чтобы только вызывался конструктор? Почему запись
Код:
res.Foo(x,f(x));
ошибочна? (ошибка
gcc писал(а):
invalid use of `class Foo'
)

 Профиль  
                  
 
 
Сообщение21.02.2008, 00:04 


21/03/06
1545
Москва
Цитата:
Ну так это лишнее - создавать второй объект, когда нужен один. Как записать, чтобы только вызывался конструктор?

В записи
Код:
res = Foo( x, f(x) );

необходимо четко разделять создание нового объекта и присваивание(копирование) объекту res только что созданного объекта. Боюсь, что мы углубимся в дебри, но опять подчеркну, уже в этой теме, возможность перегрузки оператора присваивания Foo& operator=(const Foo&). Поэтому компилятор должен сначала создать новый объект, а потом выполнить Foo.operator=, это бывает принципиально, поэтому в общем случае компилятор не может сразу так вот создавать на месте res новую инстанцию объекта Foo.

Цитата:
Ну так это лишнее - создавать второй объект, когда нужен один.

Согласен с Вами.

Цитата:
Как записать, чтобы только вызывался конструктор? Почему запись
Код:
res.Foo(x,f(x));

ошибочна?

Запись ошибочна потому, что в Си++ запрещено вызывать конструктор класса (также как и деструктор) напрямую. Они вызываются либо автоматически, как в Вашем случае приведения типов, либо с помощью операторов new/delete при динамическом создании объектов.

Проблема Вашего класса, насколько я понял, в отсутствии, кроме конструктора, функции преобразования значений x, f(x) в объект класса Foo. Если Вы не имеете доступа к реализации класса Foo, то Вам ничего не остается, как создавать объект каждый раз заново.

От операции присваивания(копирования) объекта Foo избавиться можно, если воспользоваться методом, предложенным maxal'ом, т.е. вместо присваивания объекта Foo другому объекту Foo можно присваивать адрес созданного объекта Foo указателю, это не ресурсоемкая операция. Однако, не рекомендую. Криво ужасно получается, деструктор где-то вызывать и т.п. Может быть, Вам стоит перекомпановать Вашу функцию, чтобы в ней уже производились какие-то полезные действия с вновьсозданным объектом, и в ней же он потом и удалялся?

Вопрос в другом - а настолько ли это принципиально? Память тут роли не играет (если у Вас конечно одна инстанция объекта не занимает половину доступной памяти :D ), т.к. память, отводимая под новый объект, сразу же после операции присваивания освобождается. Скорость - вот что надо оценить. Если у Вас подобные действия происходят в цикле, объект сложный, то есть смысл думать, как бы пооптимизировать. А если операция присваивания в среднем занимает, скажем, 0,1% от всего времени работы программы, то и думать над ней не нужно, пока Вы не оптимизируете более ресурсоемкие участки кода.

 Профиль  
                  
 
 
Сообщение19.03.2008, 02:22 


08/01/08
21
e2e4 писал(а):
Кстати, проблему можно решить путем создания статического объекта типа Foo в вашей функции, присвоения значения Foo(x, f(x)) этому объекту, и возвращения ссылки на него. Это значительно лучше, чем new, предложенный maxal'ом, хотя, конечно, все зависит от конкретики.


Довольно часто, в таких случаях, static -- зло, потому что не thread-safe. Если в программу потом кода-нибудь придётся добавить многопоточность, то придётся переделать эту функцию так, чтобы она возвращала указатель. А это приведёт к тому, что менять придётся и все места вызова этой функции.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 18 ]  На страницу 1, 2  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group