|
Программирование >> Решение нетривиальных задач
информации. Это не значит, что вы должны избегать ссылок. Четвертая причина в начале этого раздела вполне законна: ссылки являются замечательным способом избегать ненужных затрат на копирование, неявных при передаче по значению. Тем не менее, для обеспечения безопасности ссылочные аргументы должны всегда ссылаться на константные объекты. Для данного прототипа: f( const some class &obj ); этот код вполне законен: some class an object; f( an object ); Он похож на вызов по значению и при этом, что более важно, действует подобно вызову по значению - модификатор const предотвращает модификацию an object в функции f(). Вы получили эффективность вызова по ссылке без его проблем. Подведем итог: Я решаю, нужно или нет использовать ссылку, вначале игнорируя факт существования ссылок. Входные аргументы функций передаются по значению, а выходные - используют указатели на то место, где будут храниться результаты. Я затем преобразую те аргументы, которые передаются по значению, в ссылки на константные объекты, если эти аргументы: являются объектами какого-то класса (в отличие от основных типов, подобных int); не модифицируются где-то внутри функции. Объекты, которые передаются по значению и затем модифицируются внутри функции, конечно должны по-прежнему передаваться по значению. В заключение этого обсуждения рассмотрим пример из реальной жизни того, как не надо использовать ссылки. Объект CDocument содержит список объектов CView. Вы можете получить доступ к элементам этого списка следующим образом: CDocument *doc; CView *view; POSITION pos = doc->GetFirstViewPosition(); while( view = GetNextView(pos) ) view->Invalidate(); Здесь есть две проблемы. Во-первых, у функции GetNextView() неудачное имя. Она должна быть названа GetCurrentViewAndAdvancePosition(), потому что она на самом деле возвращает текущий элемент и затем продвигает указатель положения (который является ссылочным аргументом результата) на следующий элемент. Что приводит нас ко второй проблеме: средний читатель смотрит на предыдущий код и задумывается над тем, как завершается этот цикл. Другими словами, здесь скрывается сюрприз. Операция итерации цикла скрыта в GetNextView(pos), поэтому неясно, где она происходит. Ситуация могла быть хуже, если бы цикл был больше и содержал бы несколько функций, использующих pos в качестве аргумента - вы бы не имели никакого представления о том, какая из них вызывает перемещение. Есть множество лучших способов решения этой проблемы. Простейший заключается в использовании в качестве аргумента GetNextView() указателя вместо ссылки: POSITION pos = doc->GetFirstViewPosition(); while( p = GetNextView( &pos ) ) p->Invalidate(); Таким способом &pos сообщает вам, что pos будет модифицироваться; иначе зачем передавать указатель? Тем не менее, существуют и лучшие решения. Вот первое: for( CView *p = doc->GetFirstView(); p ; p = p->NextView() ) p->Invalidate(); Вот второе: POSITION pos = doc->GetFirstViewPosition(); for( ; pos ; pos = doc->GetNextView(pos) ) (pos->current())->Invalidate(); Вот третье: CPosition pos = doc->GetFirstViewPosition(); for( ; pos; pos.Advance() ) ( pos->CurrentView() )->Invalidate(); Вот четвертое: ViewListIterator cur view = doc->View list(); Просмотреть весь список отображений этого документа. for( ; cur view ; ++cur view ) ++ переходит к следующему отображению. cur view->Invalidate(); -> возвращает указатель View*. Вероятно, есть еще дюжина других возможностей. Все предыдущее варианты обладают требуемым свойством - в них нет скрытых операций и ясно, как происходит переход к текущему положению . 122. Не возвращайте ссылки (или указатели) на локальные переменные Эта проблема проявляется и в Си, где вы не можете вернуть указатель на локальную переменную. Не возвращайте ссылку на объект, который не существует после этого возврата. Следующий код не работает: some class &f() some class x; ... return x; Действительной проблемой здесь является синтаксис Си++. Оператор return может располагаться на отдалении от определения возвращаемой величины. Единственный способ узнать, что на самом деле делает return x, - это взглянуть на заголовок функции и посмотреть, возвращает она ссылку, или нет. 123. Не возвращайте ссылки на память, выделенную оператором new Каждый вызов new должен сопровождаться delete - подобно malloc() и free(). Я иногда видел людей, старающихся избежать накладных расходов от конструкторов копии перегруженной бинарной операции подобным образом: const some class &some class::operator+( const some class &r ) const some class *p = new some class; ... return *p; Этот код не работает, потому что вы не можете вернуться к этой памяти, чтобы освободить ее. Когда вы пишите: some class a, b, c; c = a + b; то a + b возвращает объект, а не указатель. Единственным способом получить указатель, который вы можете передать в оператор delete, является:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |