Перейти к содержимому

Как вернуть ссылку на объект c

  • автор:

Как работает возврат ссылки?

Что-то я туплю. Есть класс буфера, который, возвращая элемент, копирует его в свой член OutObject и возвращает ссылку на OutObject. Принимается же этот элемент в объект:

struct DataStr; DataStr& TBuffer::get( void ) < // . OutObject = *OldElement; return OutObject; >DataStr data; data = buffer->get(); 

К какой области памяти теперь относится data? К той же, что и раньше? Или к OutObject? Или здесь все же происходит копирование значения?

Отслеживать
задан 27 дек 2012 в 6:26
137 2 2 золотых знака 2 2 серебряных знака 9 9 бронзовых знаков

— При выполнении метода get для OutObject будет вызван деструктор и operator=(*OldElement) , определенный явно или неявно. — После выполнения этого кода для data будет вызван оператор присваивания с параметром-ссылкой на результат метода get .

27 дек 2012 в 10:16
Не понял, какой еще деструктор? Получается, ссылку возвращать бесполезно? Можно просто объект?
27 дек 2012 в 11:26

@ProkletyiPirat В строчке DataStr data; будет вызван конструктор для без параметров DataStr::DataStr() . В том случае, если этот вызов можно доказуемо соптимизировать, то, наверно, этого вызова не произойдет, но потенциально он имеет место быть. @cyrax Вообще, метод какой-то странный и путающий. Неконстантный getter, который содержит какую-то логику — это всегда плохо. Не думайте о количестве копирований (поскольку есть RVO и прочие полезные штуки), лучше задумайтесь о том, как сделать метод get константным и прозрачным. Возвращать объект можно и по значению.

27 дек 2012 в 12:53

Я лично так и не понял какого типа переменные OutObject и OldElement . Без этого дальнейший разговор бессмысленен.

28 дек 2012 в 21:21

@cyrax, Вы на самом деле чего хотите? Хранить данные в памяти экземпляра объекта и обращаться к этой памяти? Тогда так и код напишите. Возвращайте указатель на эти байты и не морочьте (прежде всего себе самому) голову. Если хотите копировать, тогда явно (например, вызвав memcpy()) копируйте. Делайте просто и понятно. Про KISS принцип слышали?

29 дек 2012 в 6:07

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

о боже, ответ получился безумно длинным и без BBcode выглядит ужасно нечитабельно :((

не пугайтесь огромного размера :))

предисловие

чтобы не было путаницы ввиду пару обозначений $ //некое место в коде (может иметь номер $1,$2) переменная //именованная область данных (переменная с именем) ^ // временная переменная без имени но с адресом в памяти адрес_объекта //адрес в памяти какого-нибудь объекта адрес_переменной //адрес какой то переменной переменная_указатель //переменная типа указатель переменная_ссылка //переменная типа ссылка переменная_объект //переменная типа объект int переменная_объект //переменная типа int int переменная_указатель //указатель на int int переменная_ссылка //ссылка на int (текст) //какие то мысли или пояснение 

что такое ссылка ?

все ссылки это по сути константные указатели (не путать с указателем на конст)

тип * const имя_переменной_указатель 

у ссылок есть пара отличий от просто указателя

  1. нельзя менять адрес_объекта
  2. адрес_объекта не может быть равным нулю
  3. тип ссылки и объекта должен быть одинаковым
  4. адрес_объекта можно назначить только во время создания ссылки

во время компиляции, компилятор автоматически переделает ссылку в конст_переменная_указатель , возьмёт адрес_объекта и во всех местах где используется ссылка подставит разыменование ( * конст_переменная_указатель ).

передача\возврат значений в\из функцию

главное всегда помнить в функцию ничего не передаётся и функция ничего не возвращает

на самом деле вы создаёте в функции новую переменную и присваиваете ей значение из вызвавшей_функции (эта переменная может быть указателем, ссылкой или объектом)

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

создаём в функции новые переменные

—————пример1:
void fun(тип переменная_1_объект) $ < \\. >\\--------------------------------\\ fun(переменная_объект); 

при вызове fun() в месте $ будет выполнена операция

тип переменная_1_объект = переменная_объект 
—————пример2:
void fun(тип &переменная_1_ссылка) $ < \\. >\\--------------------------------\\ fun(переменная_объект); 

при вызове fun() в месте $ будет выполнена операция

тип &переменная_1_ссылка = переменная_объект 
—————пример3: (работать не будет)
void fun(тип *переменная_1_указатель) $ < \\. >\\--------------------------------\\ fun(переменная_объект); 

при вызове fun() в месте $ будет выполнена операция

тип *переменная_1_указатель = переменная_объект //вот тут то и неработает 

создаём в вызвавшей_функции переменные без имени «^»

—————пример1:
тип fun() < \\. return переменная_1_объект; >\\--------------------------------\\ $ fun(); 

в вызвавшей_функции в месте $ будет выполнена операция

тип ^_объект = переменная_1_объект 
—————пример2:

в вызвавшей_функции в месте $ будет выполнена операция

тип &^_ссылка = переменная_1_объект 
—————пример3: (работать не будет)
тип* fun() < \\. return переменная_1_объект; >\\--------------------------------\\ $ fun(); 

в вызвавшей_функции в месте $ будет выполнена операция

тип *^_указатель= переменная_1_объект //вот тут то и неработает 

смешиваем

—————пример:

в функции_fun в месте $1 будет выполнена операция

тип &переменная_1_ссылка = переменная_объект 

затем в вызвавшей_функции в месте $2 будет выполнена операция

тип &^_ссылка = (переменная_1_ссылка) 

тоесть ^_ссылка и переменная_1_ссылка будут указывать на переменная_объект

если кто не понял привожу аналогичный код в виде указателей

в функции_fun в месте $1 будет выполнена операция

тип * const переменная_1 = &переменная //(объект) 

затем в вызвавшей_функции в месте $2 будет выполнена операция

тип * const ^ = &(*переменная_1) // 

p.s стоит ли писать про то когда вызывается конструктор_копирования , а когда оператор= .

p.s.s вопросы, недочёты, ошибки и т.д. писать в коментах под этим постом

p.s.s.s пусть «очень много «пожеланий»» создателей Markdown

Как вернуть ссылку на объект c

Если некоторая функция которая должна возвращать ссылку на объект.
Если две проблемы:
— если возвращать так:

CAnyObject& AnyFunction ()
return AnyObject ();
>
то объект разрушится после очистки стека и им нельзя будет пользоваться

— если так:
CAnyObject& AnyFunction ()
return *(new AnyObject);
>
то все работает но память естественно не чистится.

Подскажите пожалуйста как правильно возвращать ссылку на объект.

Re: Как правильно вернуть ссылку на объект?

От: Denwer
Дата: 05.08.05 06:28
Оценка:

Здравствуйте, rbu, Вы писали:

rbu>Если некоторая функция которая должна возвращать ссылку на объект.
rbu>Если две проблемы:
rbu>— если возвращать так:

rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return AnyObject ();
rbu> >
rbu> то объект разрушится после очистки стека и им нельзя будет пользоваться

rbu>— если так:
rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return *(new AnyObject);
rbu> >
rbu> то все работает но память естественно не чистится.

rbu>Подскажите пожалуйста как правильно возвращать ссылку на объект.

CAnyObject& AnyFunction () < static CAnyObject obj; return obj; >

Re[2]: Как правильно вернуть ссылку на объект?

От: A. Fedotov
Дата: 05.08.05 06:38
Оценка: +1

Здравствуйте, Denwer, Вы писали:

rbu>>Подскажите пожалуйста как правильно возвращать ссылку на объект.

D>

D>CAnyObject& AnyFunction () D> < D>static CAnyObject obj; D> return obj; D>> D>

как по мне конечно работает , но как-то не хорошо, напр.

CAnyObject& AnyFunction () < static CAnyObject obj; obj++; // или как-то меняем его return obj; > . CAnyObject& si = AnyFunction(); // у si одно значение AnyFunction(); // теперь другое

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

Re: Как правильно вернуть ссылку на объект?

От: Bell
Дата: 05.08.05 06:55
Оценка:

Здравствуйте, rbu, Вы писали:

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

Любите книгу — источник знаний (с) М.Горький
Re: Как правильно вернуть ссылку на объект?

От: LuciferMoscow
Дата: 05.08.05 07:02
Оценка:

Здравствуйте, rbu, Вы писали:

std::auto_ptr AnyFunction ()
return std::auto_ptr(new AnyObject);
>

Re: Как правильно вернуть ссылку на объект?

От: Аноним
Дата: 05.08.05 07:02
Оценка:

Здравствуйте, rbu, Вы писали:

rbu>Если некоторая функция которая должна возвращать ссылку на объект.
rbu>Если две проблемы:
rbu>— если возвращать так:

rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return AnyObject ();
rbu> >
rbu> то объект разрушится после очистки стека и им нельзя будет пользоваться

rbu>— если так:
rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return *(new AnyObject);
rbu> >
rbu> то все работает но память естественно не чистится.

rbu>Подскажите пожалуйста как правильно возвращать ссылку на объект.

При создании объекта возвращать ссылку не совсем корректно. Колееги правы по поводу указателя, можно даже и обычный.

Re[2]: Как правильно вернуть ссылку на объект?

От: Bell
Дата: 05.08.05 07:17
Оценка: +2

Здравствуйте, Аноним, Вы писали:

А>При создании объекта возвращать ссылку не совсем корректно. Колееги правы по поводу указателя, можно даже и обычный.

Можно и обычный, но это сразу насторожит пользователя функции — мол а надо ли потом убивать объект? и если надо — то кто же будет это делать? А с умным указателем спокойнее — забот меньше.

Любите книгу — источник знаний (с) М.Горький
Re[3]: Как правильно вернуть ссылку на объект?

От: Константин Ленин
Дата: 05.08.05 07:50
Оценка:

Здравствуйте, Bell, Вы писали:

B>Здравствуйте, Аноним, Вы писали:

А>>При создании объекта возвращать ссылку не совсем корректно. Колееги правы по поводу указателя, можно даже и обычный.

B>Можно и обычный, но это сразу насторожит пользователя функции — мол а надо ли потом убивать объект? и если надо — то кто же будет это делать? А с умным указателем спокойнее — забот меньше.

Не совсем согласен. Как я понял этя ф-я — типа фабрики объектов и удалать должен «пользователь». А если это будет библиотечная ф-я — то надо описать правила использования.

Re[4]: Как правильно вернуть ссылку на объект?

От: Bell
Дата: 05.08.05 07:57
Оценка:

Здравствуйте, Константин Ленин, Вы писали:

КЛ>Не совсем согласен. Как я понял этя ф-я — типа фабрики объектов и удалать должен «пользователь». А если это будет библиотечная ф-я — то надо описать правила использования.

ИМХО фабрика это или что-то другое — значение не имеет. Естественно, в документации можно/нужно указать, что ответственность за удаление порожденных объектов лежит на пользователе. Естественно, существуют ситуации, когда это оправдано (из соображений эффективности, например). Но, опять же ИМХО, лучше снять с пользователя эту заботу. У него, как и у любого программиста, и своих забот помимо этой хватит.

Любите книгу — источник знаний (с) М.Горький
Re: Как правильно вернуть ссылку на объект?

От: Glоbus
Дата: 05.08.05 08:08
Оценка:

Здравствуйте, rbu, Вы писали:

rbu>Если некоторая функция которая должна возвращать ссылку на объект.
rbu>Если две проблемы:
rbu>— если возвращать так:

rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return AnyObject ();
rbu> >
rbu> то объект разрушится после очистки стека и им нельзя будет пользоваться

rbu>— если так:
rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return *(new AnyObject);
rbu> >
rbu> то все работает но память естественно не чистится.

rbu>Подскажите пожалуйста как правильно возвращать ссылку на объект.

Может попробовать как-нить так:

std::auto_ptr AnyFunction() < return std::auto_ptr( new CAnyObject() ); >

Удачи тебе, браток!
Re: Как правильно вернуть ссылку на объект?

От: Centaur
Дата: 05.08.05 14:47
Оценка:

Здравствуйте, rbu, Вы писали:

rbu>Если некоторая функция которая должна возвращать ссылку на объект.

rbu>Если две проблемы:
rbu>— если возвращать так:

rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return AnyObject ();
rbu> >
rbu> то объект разрушится после очистки стека и им нельзя будет пользоваться

rbu>— если так:
rbu> CAnyObject& AnyFunction ()
rbu> <
rbu> return *(new AnyObject);
rbu> >
rbu> то все работает но память естественно не чистится.

rbu>Подскажите пожалуйста как правильно возвращать ссылку на объект.

Лучше всего никак. И именно из-за этих двух проблем.

Если функция должна создавать объект, пусть она возвращает его по значению. Как std::make_pair. Но если объект тяжело копируемый, то пусть возвращает по указателю. Но если функция возвращает указатель, то у пользователя начинают возникать вопросы, а кто и когда и должен ли на этот указатель сказать delete (или delete[]). Поэтому пусть указатель будет умным. Как, например, std::auto_ptr.

Re[5]: Как правильно вернуть ссылку на объект?

От: dad
Дата: 05.08.05 18:13
Оценка:

КЛ>>Не совсем согласен. Как я понял этя ф-я — типа фабрики объектов и удалать должен «пользователь». А если это будет библиотечная ф-я — то надо описать правила использования.

B>ИМХО фабрика это или что-то другое — значение не имеет. Естественно, в документации можно/нужно указать, что ответственность за удаление порожденных объектов лежит на пользователе. Естественно, существуют ситуации, когда это оправдано (из соображений эффективности, например). Но, опять же ИМХО, лучше снять с пользователя эту заботу. У него, как и у любого программиста, и своих забот помимо этой хватит.

тонны времени уходят потом такие гуляющие указатели отслеживать, куда ушел, кому передали. нужно делать симметричную уничтожающую 100% или же аутопоинтер

Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)

Возвращаемые значения функции ссылочного типа

Функции можно объявить, чтобы они возвращали ссылочный тип. Существует две причины создания такого объявления.

  • Возвращаемая информация представляет собой настолько крупный объект, что возврат ссылки является более эффективным, чем возврат копии.
  • Тип функции должен представлять собой l-значение.
  • Объект, на который указывает ссылка, не выйдет из области видимости при возврате управления функцией.

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

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

Пример

Рассмотрим пример Point .

// refType_function_returns.cpp // compile with: /EHsc #include using namespace std; class Point < public: // Define "accessor" functions as // reference types. unsigned& x(); unsigned& y(); private: // Note that these are declared at class scope: unsigned obj_x; unsigned obj_y; >; unsigned& Point :: x() < return obj_x; >unsigned& Point :: y() < return obj_y; >int main() < Point ThePoint; // Use x() and y() as l-values. ThePoint.x() = 7; ThePoint.y() = 9; // Use x() and y() as r-values. cout

Выходные данные

x = 7 y = 9 

Обратите внимание, что функции x и y объявляются как типы возвращаемых ссылок. Эти функции можно использовать на любой стороне оператора присваивания.

Также обратите внимание, что в функции main объект ThePoint остается в области видимости, поэтому его ссылочные элементы продолжают действовать, и к ним можно получить безопасный доступ.

Объявления ссылочных типов должны содержать инициализаторы. Исключение составляют следующие случаи.

  • Явное extern объявление
  • Объявление члена класса
  • Объявление в классе
  • Объявление аргумента в адрес функции или типа возвращаемого значения для функции

Предупреждение при возвращении адреса локальной переменной

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

// C4172 means Don't do this. Foo& GetFoo() < Foo f; . return f; >// f is destroyed here 

Компилятор выдает предупреждение в этом случае: warning C4172: returning address of local variable or temporary В простых программах доступ может случайно сохраниться, если ссылка будет использована вызывающим объектом до перезаписи соответствующей области памяти. Однако это чистая случайность. Обратите внимание на предупреждение.

Правило 21: Не пытайтесь вернуть ссылку, когда должны вернуть объект

Как только программисты осознают проблемы эффективности, связанные с передачей объектов по значению (см. правило 20), они, подобно крестоносцам, преисполняются решимости искоренить зло – передачу по значению – везде, где бы оно ни пряталось. Непреклонные в своем «святом» порыве, они с неизбежностью допускают фатальную ошибку: начинают передавать по ссылке значения несуществующих объектов. А это неправильно.

Рассмотрим класс для представления рациональных чисел, включающий в себя дружественную функцию для перемножения двух таких чисел:

Rational(int numerator = 0, // см. в правиле 24 – почему этот

int denominator = 1); // конструктор не explicit

const Rational // см. в правиле 3 -

operator*(const Rational& lhs, // почему возвращаемый тип const

Ясно, что эта версия operator* возвращает результирующий объект по значению, и вы обнаружили бы непрофессиональный подход, если бы не уделили внимания вопросу о затратах на создание и удаление объекта. Вы не хотите платить за то, за что платить не должны. Отсюда вопрос: должны ли вы платить?

Нет, если можете вернуть ссылку. Но ссылка – это просто другое имя некоторого существующего объекта. Всякий раз, сталкиваясь с объявлением ссылки, вы должны спросить себя: для чего предназначено это имя, ведь оно должно принадлежать чему-то. В случае operator*, если функция возвращает ссылку, значит, она должна вернуть ссылку на некоторый уже существующий объект Rational, который и содержит произведение двух объектов, которые следовало перемножить.

Очевидно, нет никаких оснований полагать, что такой объект существует до вызова operator*. Например, если у вас есть

Rational a(1, 2); // a = 1/2

Rational a(3, 5); // b = 3/5

Rational c = a*b; // c должно равняться 3/10

то неразумно ожидать, что уже существует то рациональное число со значением три десятых. Если operator* будет возвращать такое число, то он должен создать его самостоятельно.

Функция может создать новый объект только двумя способами: в стеке или в куче. Создание в стеке осуществляется посредством определения локальной переменной. Используя эту стратегию, вы можете попытаться написать operator* так:

const Rational& operator*(const Rational& lhs, // предупреждение!

const Rational& rhs) // плохой код!

Rational result(lhs.n * rhs.h, lhs.d * rhs.d);

Этот подход можно отвергнуть сразу, потому что вашей целью было избежать вызова конструктора, а result должен быть создан, подобно любому другому объекту. Кроме того, эта функция порождает и более серьезную проблему, поскольку возвращает ссылку на result, но result – это локальный объект, а локальные объекты разрушаются при завершении функции, в которой они объявлены. Таким образом, эта версия operator* возвращает ссылку не на Rational, а на бывший Rational – пустой, отвратительный, гнилой скелет того, что когда-то было объектом Rational, но уже не является таковым, потому что он уничтожен. Стоит вызвать эту функцию – вы попадете в область неопределенного поведения. Запомним: любая функция, которая возвращает ссылку на локальный объект, некорректна (то же касается и функций, возвращающих указатель на локальный объект).

А теперь давайте рассмотрим возможность конструирования объекта в «куче» с возвратом ссылки на него. Объекты в «куче» создаются посредством new. Вот как мог бы выглядеть operator* в этом случае:

const Rational& operator*(const Rational& lhs, // предупреждение!

const Rational& rhs) // Опять плохой код!

Rational *result = new Rational(lhs.n * rhs.h, lhs.d * rhs.d);

Да, вам все же придется расплачиваться за вызов конструктора, поскольку память, выделяемая new, инициализируется вызовом соответствующего конструктора, но теперь возникает новая проблема: кто выполнит delete для объекта, созданного вами с использованием new?

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

Rational w, x, y, z;

w = x * y * z; // то же, что operator*(operator*(x, y), z)

Здесь выполняется два вызова operator* в одном предложении, поэтому получаются два вызова new, которым должны соответствовать два delete. Но у пользователя operator* нет возможности это сделать, так как он не может получить указатели, скрытые за ссылками, которые возвращает функция operator*. Это гарантированная утечка ресурсов.

Но, возможно, вы заметили, что оба подхода (на основе стека и на основе кучи) страдают от необходимости вызова конструкторов для каждого возвращаемого значения operator*. Вспомните, что исходно мы ставили себе целью вообще не вызывать конструкторы. Быть может, вы думаете, что знаете, как избежать всего, всех вызовов конструктора, кроме одного. Не исключено, что вы придумали следующую реализацию функции operator*, которая возвращает ссылку на статический объект Rational, определенный внутри функции:

const Rational& operator*(const Rational& lhs, // предупреждение!

const Rational& rhs) // Код еще хуже!

static Rational result; // статический объект,

// на который возвращается ссылка

result = . ; // умножить lhs на rhs и поместить

// произведение в result

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

bool operator==(const Rational& lhs, // оператор == для Rational

Rational a, b, c, d;

действия, необходимые в случае, если два произведения равны;

действия, необходимые в противном случае;

Догадываетесь, что не так? Выражение ((a*b) == (c*d)) будет всегда равно true независимо от значений a, b, c и d!

Легче всего найти объяснение такому неприятному поведению, переписать проверку на равенство в эквивалентной функциональной форме:

if(operator==(operator*(a, b), operator*(c, d)))

Заметьте, что когда вызывается operator==, уже присутствуют два активных вызова operator*, каждый из которых будет возвращать ссылку на статический объект Rational внутри operator*. Таким образом, operator== будет сравнивать статический объект Rational, определенный в функции operator*, со значением статического объект Rational внутри той же функции. Было бы удивительно, если бы они не оказались равны всегда.

Этого должно быть достаточно, чтобы убедить вас, что возвращение ссылки из функции, подобной operator*, – пустая трата времени, но я не настолько наивен, чтобы полагаться на везение. Кое-кто в настоящий момент думает: «Хорошо, если недостаточно одного статического объекта, то, может быть, для этого подойдет статический массив…»

Я не снизойду до того, чтобы посвятить такой программе отдельный пример, но вкратце могу пояснить, почему даже возникновение такой идеи должно повергать вас в стыд. Во-первых, вы должны выбрать n – размер массива. Если n слишком мало, у вас может закончиться место для хранения, и вы ничего не выиграете по сравнению с вышеописанной программой. Если же n чересчур велико, вы уменьшаете производительность вашей программы, поскольку каждый объект в массиве конструируется при первом вызове функции. Это будет стоить вам n вызовов конструкторов и n вызовов деструкторов, даже если данная функция вызывается всего один раз. Если процесс повышения производительности программного обеспечения называется оптимизацией, тогда самое верное название происходящему – «пессимизация». И наконец, подумайте о том, как заносить необходимые вам значения в массив объектов и во что это обойдется. Наиболее прямой способ передачи объектов – операция присваивания, но с чем она связана? В общем случае это вызов деструктора (для уничтожения старого значения) плюс вызов конструктора (для копирования нового значения). А ваша цель – избежать вызовов конструктора и деструктора! Так что затея весьма неудачна (нет-нет: применение векторов вместо массивов не улучшит ситуацию).

Правильный способ написания функции заключается в том, что она должна возвращать новый объект. В применении к operator* для класса Rational это означает либо следующий код, либо нечто похожее:

inline const Rational operator*(const Rational& lhs, Rational& rhs)

return Rational(lhs.n*rhs.h, lhs.d*rhs.d);

Конечно, в этом случае вам придется смириться с издержками на вызов конструктора и деструктора для объектов, возвращаемых operator*, но в глобальном масштабе это небольшая цена за корректное поведение. Притом, вероятно, все не так уж страшно. Подобно всем языкам программирования, C++ позволяет разработчикам компиляторов применить оптимизацию для повышения производительности генерируемого кода, и, как оказывается, в некоторых случаях вызовы конструктора и деструктора возвращаемого operator* значения можно безопасно устранить. Когда компилятор пользуется этой возможностью (а часто он так и поступает), ваша программа продолжает делать то, чего вы от нее хотите, и даже быстрее, чем ожидалось.

Подведем итог: когда вы выбираете между возвратом ссылки и возвращением объекта, ваша задача заключается в том, чтобы все работало правильно. О том, как сделать этот выбор менее накладным, должен заботиться разработчик компилятора.

Что следует помнить

• Никогда не возвращайте указатель или ссылку на локальный объект, ссылку на объект, распределенный в «куче», либо указатель или ссылку на локальный статический объект, если есть шанс, что понадобится более, чем один экземпляр такого объекта. В правиле 4 приведен пример ситуации, когда возврат ссылки на локальный статический объект имеет смысл, по крайней мере, в однопоточных средах.

Данный текст является ознакомительным фрагментом.

Продолжение на ЛитРес

Читайте также

Не пытайтесь

Не пытайтесь Худшее, что может сделать Пола в ответ на манипуляции Майка, – сказать: «Хорошо, я попытаюсь». Не хочу приплетать сюда Йоду, но в данном случае он прав. Не надо пытаться.Вам не нравится эта мысль? Может, вы думаете, что пытаться что-то сделать полезно? Ведь

(3.35) После изменения прав доступа к файлам (security permissions) на NTFS хочется вернуть все к тому виду, как было после установки. Возможно ли это?

(3.35) После изменения прав доступа к файлам (security permissions) на NTFS хочется вернуть все к тому виду, как было после установки. Возможно ли это? Да, это возможно. Более того, это просто необходимо, если вы поставили W2k на FAT или FAT32, а затем отконвертировали файловую систему в NTFS (см.

Как создать magnet-ссылку

Как создать magnet-ссылку Magnet-ссылка создается очень просто. Здесь могут быть два варианта: вы создаете ссылку на раздаваемые вами файлы или вы создаете ссылку на файлы, которые желаете закачать к себе на компьютер. Технология создания ссылки одна. Вам следует открыть список

2.17.После изменения прав доступа к файлам (security permissions) на NTFS хочется вернуть все к тому виду, как было после установки. Возможно ли это?

2.17.После изменения прав доступа к файлам (security permissions) на NTFS хочется вернуть все к тому виду, как было после установки. Возможно ли это? Да, это возможно. Более того, это просто необходимо, если вы поставили XP на FAT или FAT32, а затем cконвертировали файловую систему в NTFS. Для

Не пытайтесь заработать на чужой рекламе

Не пытайтесь заработать на чужой рекламе Показывая на своем сайте рекламу AdSense или «Яндекс. Директ», вы теряете много профессиональных покупателей в Интернете, которые часто и охотно покупают продукцию в интернет-магазинах. Это сбивает их с толку.Если вы действительно

Как, разместив одну ссылку на сайт, получить еще сто

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

1.6.12. Правило исправности: когда программа завершается аварийно, это должно происходить явно и по возможности быстро

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

Правило 10: Операторы присваивания должны возвращать ссылку на *this

Правило 10: Операторы присваивания должны возвращать ссылку на *this Одно из интересных свойств присваивания состоит в том, что такие операции можно выполнять последовательно:int x,y,z;x = y = z = 15; // цепочка присваиванийТакже интересно, что оператор присваивания

Правило 24: Объявляйте функции, не являющиеся членами, когда преобразование типов должно быть применимо ко всем параметрам

Правило 24: Объявляйте функции, не являющиеся членами, когда преобразование типов должно быть применимо ко всем параметрам Во введении я отмечал, что в общем случае поддержка классом неявных преобразований типов – неудачная мысль. Но, конечно, из этого правила есть

Правило 46: Определяйте внутри шаблонов функции, не являющиеся членами, когда желательны преобразования типа

Правило 46: Определяйте внутри шаблонов функции, не являющиеся членами, когда желательны преобразования типа В правиле 24 объясняется, почему только к свободным функциям применяются неявные преобразования типов всех аргументов. В качестве примера была приведена функция

Правило 50: Когда имеет смысл заменять new и delete

Правило 50: Когда имеет смысл заменять new и delete Вернемся к основам. Прежде всего зачем кому-то может понадобиться подменять предлагаемые компилятором версии operator new и operator delete? Существуют, по крайней мере, три распространенные причины.• Чтобы обнаруживать ошибки

1.6.12. Правило исправности: когда программа завершается аварийно, это должно происходить явно и по возможности быстро

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

OIT и OAT должны постоянно изменяться

OIT и OAT должны постоянно изменяться Совет "OIT и OAT должны постоянно изменяться" является ключевой фразой для решения всех проблем, связанных с производительностью базы данных. Время, потраченное на изучение цикла жизни транзакций в многоверсионной архитектуре Firebird, будет

Что вы должны знать

Что вы должны знать Данная книга представляет собой не учебник по Flash, а практическое руководство по изучению Flash 8 ActionScript. Подразумевается, что вы уже немного знакомы с рабочей средой Flash и имеете какой-то опыт работы с программой.При этом вы, также как и я, не обязаны быть

10. Когда открыть, а когда закрыть

10. Когда открыть, а когда закрыть Рассмотрев деловые модели, которые поддерживают разработку программного обеспечения с открытыми текстами, мы можем теперь приблизиться к общему вопросу о том, когда исходному коду, с точки зрения экономики, имеет смысл быть «открытым», а

Будущее вернуть Сергей Тихомиров

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *