Является ли практика возвращения ссылочной переменной C ++ злом?

Думаю, это немного субъективно. Я не уверен, будет ли мнение единогласным (я видел много fragmentов кода, где ссылки возвращаются).

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

Меня это волнует, поскольку я следовал примерам (если только не представляю себе вещи) и делал это в нескольких местах … Не понял ли я? Это зло? Если да, то как зло?

Я чувствую, что из-за моей смешанной сумки указателей и ссылок в сочетании с тем, что я новичок в C ++, и полная путаница в отношении того, что использовать, когда мои приложения должны быть утечкой памяти …

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

В общем, возrotation ссылки совершенно нормально и происходит все время.

Если ты имеешь ввиду:

int& getInt() { int i; return i; // DON'T DO THIS. } 

Это все виды зла. Выделенный стек, i уйду, и вы ничего не говорите. Это тоже зло:

 int& getInt() { int* i = new int; return *i; // DON'T DO THIS. } 

Потому что теперь клиент должен в конце концов сделать странное:

 int& myInt = getInt(); // note the &, we cannot lose this reference! delete &myInt; // must delete...totally weird and evil int oops = getInt(); delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original 

Обратите внимание, что ссылки rvalue по-прежнему являются просто ссылками, поэтому все злые приложения остаются неизменными.

Если вы хотите выделить что-то, что выходит за frameworks функции, используйте смарт-указатель (или, в общем, контейнер):

 std::unique_ptr getInt() { return std::make_unique(0); } 

И теперь клиент хранит смарт-указатель:

 std::unique_ptr x = getInt(); 

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

 struct immutableint { immutableint(int i) : i_(i) {} const int& get() const { return i_; } private: int i_; }; 

Здесь мы знаем, что нормально возвращать ссылку на i_ потому что все, что называет нас, управляет временем жизни экземпляра classа, поэтому i_ будет жить как минимум так долго.

И, конечно, нет ничего плохого:

 int getInt() { return 0; } 

Если время жизни должно быть оставлено до вызывающего, и вы просто вычисляете значение.

Резюме: нормально возвращать ссылку, если срок жизни объекта не будет завершен после вызова.

Нет. Нет, нет, в тысячу раз нет.

Что является злом, делается ссылка на динамически выделенный объект и теряется исходный указатель. Когда вы new объект, вы берете на себя обязательство иметь гарантированное delete .

Но посмотрите на, например, operator<< :, который должен вернуть ссылку, или

 cout << "foo" << "bar" << "bletch" << endl ; 

не будет работать.

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

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

Вы можете вернуть ссылку на что-то независимое от функции, которое вы не ожидаете, что вызывающая функция возьмет на себя ответственность за удаление. Это относится к типичной функции operator[] .

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

Это не зло. Как и многие другие вещи в C ++, это хорошо, если использовать правильно, но есть много ошибок, о которых вам следует знать при использовании (например, возврат ссылки на локальную переменную).

Есть хорошие вещи, которые могут быть достигнуты с ним (например, map [name] = “hello world”)

Я нахожу ответы неудовлетворительными, поэтому я добавлю свои два цента.

Проанализируем следующие случаи:

Ошибочное использование

 int& getInt() { int x = 4; return x; } 

Это, очевидно, ошибка

 int& x = getInt(); // will refer to garbage 

Использование со статическими переменными

 int& getInt() { static int x = 4; return x; } 

Это правильно, потому что статические переменные существуют на протяжении всего жизненного цикла программы.

 int& x = getInt(); // valid reference, x = 4 

Это также довольно часто встречается при реализации шаблона Singleton

 Class Singleton { public: static Singleton& instance() { static Singleton instance; return instance; }; void printHello() { printf("Hello"); }; } 

Применение:

  Singleton& my_sing = Singleton::instance(); // Valid Singleton instance my_sing.printHello(); // "Hello" 

операторы

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

 T & operator*(); 

могут быть использованы в следующих

 std::vector x = {1, 2, 3}; // create vector with 3 elements std::vector::iterator iter = x.begin(); // iterator points to first element (1) *iter = 2; // modify first element, x = {2, 2, 3} now 

Быстрый доступ к внутренним данным

Бывают случаи, когда & могут использоваться для быстрого доступа к внутренним данным

 Class Container { private: std::vector m_data; public: std::vector& data() { return m_data; } } 

с использованием:

 Container cont; cont.data().push_back(1); // appends element to std::vector cont.data()[0] // 1 

ОДНАКО, это может привести к ловушке, например:

 Container* cont = new Container; std::vector& cont_data = cont->data(); cont_data.push_back(1); delete cont; // This is bad, because we still have a dangling reference to its internal data! cont_data[0]; // dangling reference! 

«возrotation ссылки – это зло, потому что, просто [как я понимаю», это облегчает пропустить удаление »

Не правда. Возrotation ссылки не подразумевает семантику собственности. То есть, только потому, что вы это делаете:

 Value& v = thing->getTheValue(); 

… не означает, что теперь у вас есть память, на которую ссылается v;

Однако это ужасный код:

 int& getTheValue() { return *new int; } 

Если вы делаете что-то вроде этого, потому что «вам не нужен указатель на этот экземпляр», тогда: 1) просто разыщите указатель, если вам нужна ссылка, и 2) вам в конце концов понадобится указатель, потому что вам нужно сопоставить новый с удалением, и вам нужен указатель на вызов delete.

Есть два случая:

  • const reference – хорошая идея, иногда, особенно для тяжелых объектов или classов прокси, оптимизация компилятора

  • неконстантная ссылка – идея иногда разрывает инкапсуляции

Оба имеют общую проблему – потенциально могут указывать на уничтоженный объект …

Я бы рекомендовал использовать интеллектуальные указатели для многих ситуаций, когда вам требуется вернуть ссылку / указатель.

Также обратите внимание на следующее:

Существует формальное правило – в стандарте C ++ (раздел 13.3.3.1.4, если вы заинтересованы) говорится, что временное может быть привязано только к константной ссылке – если вы попытаетесь использовать ссылку на не-const, компилятор должен отметить это как ошибка.

Мало того, что это не зло, иногда это необходимо. Например, было бы невозможно реализовать оператор [] для std :: vector без использования ссылочного возвращаемого значения.

ссылка возврата обычно используется при перегрузке оператора в C ++ для большого объекта, поскольку для возврата значения требуется операция копирования. (при перегрузке переадресатора мы обычно не используем указатель как возвращаемое значение)

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

если вы хотите использовать return refernce, вы можете использовать буфер статического объекта. например

 const max_tmp=5; Obj& get_tmp() { static int buf=0; static Obj Buf[max_tmp]; if(buf==max_tmp) buf=0; return Buf[buf++]; } Obj& operator+(const Obj& o1, const Obj& o1) { Obj& res=get_tmp(); // +operation return res; } 

таким образом, вы можете безопасно использовать возвращаемую ссылку.

Но вы всегда можете использовать указатель вместо ссылки для возврата значения в functiong.

Дополнение к принятому ответу:

 struct immutableint { immutableint(int i) : i_(i) {} const int& get() const { return i_; } private: int i_; }; 

Я бы сказал, что этот пример не подходит, и его следует избегать, если это возможно. Зачем? Это очень просто, чтобы привести в замешательство ссылки .

Чтобы проиллюстрировать эту точку примера:

 struct Foo { Foo(int i = 42) : boo_(i) {} immutableint boo() { return boo_; } private: immutableint boo_; }; 

попадание в опасную зону:

 Foo foo; const int& dangling = foo.boo().get(); // dangling reference! 

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

Лучше всего создать объект и передать его в качестве параметра reference / pointer функции, которая выделяет эту переменную.

Выделение объекта в функции и возrotation его в качестве ссылки или указателя (указатель более безопасен) – это плохая идея из-за освобождения памяти в конце функционального блока.

Функция как lvalue (ака, возврат неконстантных ссылок) должна быть удалена из C ++. Это ужасно неинтуитивно. Скотт Мейерс хотел, чтобы мин () с этим поведением.

 min(a,b) = 0; // What??? 

что на самом деле не

 setmin (a, b, 0); 

Последнее даже имеет больше смысла.

Я понимаю, что функция lvalue важна для streamов стиля C ++, но стоит отметить, что streamи стилей C ++ ужасны. Я не единственный, кто думает об этом … поскольку я помню, что у Alexandrescu была большая статья о том, как сделать лучше, и я считаю, что boost также попытался создать более безопасный метод ввода-вывода типа.

  Class Set { int *ptr; int size; public: Set(){ size =0; } Set(int size) { this->size = size; ptr = new int [size]; } int& getPtr(int i) { return ptr[i]; // bad practice } }; 

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

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

Полные подробности, о которых я писал в Janurary: http://developer-resource.blogspot.com/2009/01/pros-and-cons-of-returing-references.html

О ужасном коде:

 int& getTheValue() { return *new int; } 

Итак, действительно, указатель памяти потерян после возвращения. Но если вы используете shared_ptr следующим образом:

 int& getTheValue() { std::shared_ptr p(new int); return *p->get(); } 

Память не теряется после возвращения и будет освобождена после назначения.

  • Одна ошибка VS2010? Разрешить привязку не-const ссылки на rvalue БЕЗ ДАЖЕ предупреждение?
  • C ++: ссылка const, перед vs после спецификатора типа
  • Как скопировать содержимое одного ArrayList в другое?
  • Установить пользовательский путь к ссылочным DLL?
  • Как обратиться к документации Microsoft.Office.Interop.Excel?
  • Java: разница между сильным / мягким / слабым / фантомным ссылкой
  • Что такое «корневая ссылка»?
  • Есть ли getchas с использованием varargs со ссылочными параметрами
  • Как ссылаться на общие classы и методы в XML-документации
  • Почему C # не поддерживает возврат ссылок?
  • Как получить доступ к переменной из другого скрипта в другом игровом объекте через GetComponent?
  • Interesting Posts

    Почему принтер принтера Epson часто появляется в автономном режиме?

    Правильный способ сохранить объединенные соединения в живых (или время их выхода и получить свежие) в течение более длительного времени бездействия для MySQL, приложение Grails 2

    C # Итерирование через перечисление? (Индексирование System.Array)

    Эквивалент ShellExecute в .NET.

    Подробная информация о USB-не повезло до сих пор

    Запись / чтение файлов в / из внутренней памяти телефона Android

    Реализация пользовательских методов хранилища Spring Data и их просмотр через REST

    У встроенных типов есть стандартные конструкторы?

    Kali Linux на виртуальной коробке

    Как перебирать диапазон дат в Java?

    Как удалить или удалить html-tags в Android

    Как обновить отдельную библиотеку с помощью Composer?

    Почему мой Macbook становится очень жарким при использовании Boot Camp?

    Почему ValidateInput (False) не работает?

    Размер диска больше, чем размер файлов на диске

    Давайте будем гением компьютера.