Здравствуйте yogi, Вы писали:
Y>Я читал кажется у Элджера, что он советует использовать ссылки для лаконичности и простоты. А указатели использовать только тогда, когда передаваемый объект может не существовать (т.к. не может быть ссылки на NULL).
Ещё как может!
Т.к. при Type* GetVar(); и возврате валидного NULL для неё в функцию SomeFunc( *GetVar() ) передастся пустая ссылка и без "вполне осознаннах телодвижений — такое по ошибке не сделаешь — в отличие от пустых и некорректных указателей" TepMuHyc. Это может быть и баг VC6.0, но тем не менее ...
int* GetVar()
{
return NULL;
}
void SomeFunc( int& a )
{
int i;
if( &a == NULL )
i = 1;
else
i = a;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
// TODO: Place code here.
SomeFunc( *GetVar() );
return 0;
}
Здравствуйте Edmond, Вы писали:
E>С точки зрения низкоуровневой реализации E>Указатель -- переменная, содержащая адрес... E>Ссылка -- временная переменная..., но
E>Почувствуйте разницу...
Здравствуйте Vi2, Вы писали:
Vi2>Я достаточно часто видел и такое описание функций Vi2>
Vi2>void SomeFunc( int& a = *(int*)NULL );
Vi2>
Vi2>Так что и ссылками можно наворотить дай бог что.
Долго думал. Единственное применение, которое кажется более-менее разумным — получить access violation, если функцию вызвали с параметром по умолчанию Почему бы тогда не убрать вообще умолчание?
Здравствуйте m.a.g., Вы писали:
...>Здравствуйте Vi2, Вы писали:
Vi2>>Я достаточно часто видел и такое описание функций Vi2>>
Vi2>>void SomeFunc( int& a = *(int*)NULL );
Vi2>>
Vi2>>Так что и ссылками можно наворотить дай бог что.
...>Долго думал. Единственное применение, которое кажется более-менее разумным — получить access violation, если функцию вызвали с параметром по умолчанию Почему бы тогда не убрать вообще умолчание?
Почему же, можно проверить корректна ли ссылка (&a!=NULL) и если корректна, то можно ее и использовать.
Другой вопрос зачем это все надо? IMHO, в данном случае использование ссылкм не приносит ни каких удобств по
сравнению с использованием указателей, а только способствует запутыванию кода.
Здравствуйте deviv, Вы писали:
Vi2>>>Я достаточно часто видел и такое описание функций
Vi2>>>void SomeFunc( int& a = *(int*)NULL );
Vi2>>>Так что и ссылками можно наворотить дай бог что.
...>>Долго думал. Единственное применение, которое кажется более-менее разумным — получить access violation, если функцию вызвали с параметром по умолчанию Почему бы тогда не убрать вообще умолчание?
D>Почему же, можно проверить корректна ли ссылка (&a!=NULL) и если корректна, то можно ее и использовать. D>Другой вопрос зачем это все надо? IMHO, в данном случае использование ссылкм не приносит ни каких удобств по D>сравнению с использованием указателей, а только способствует запутыванию кода.
Возможно. На самом деле в реальном примере используются более сложные классы, чем int. Видимо тут (псевдо!)экономия на "->" и ".".
Но я бы не сказал, что это хороший стиль, что ему надо следовать. Даже наоборот так делать не надо. Однако такой код есть, и в достаточно известной фирме.
и может другие есть), что такое выражение *(int*)NULL — неопределённое поведение. То, что код компилится VC6.0 и работает — это просто счастье (короткое?) разработчиков.
Здравствуйте Vi2, Вы писали:
Vi2>Здравствуйте Edmond, Вы писали:
E>>С точки зрения низкоуровневой реализации E>>Указатель -- переменная, содержащая адрес... E>>Ссылка -- временная переменная..., но
E>>Почувствуйте разницу... Vi2>
E>>Думаю все остальные выводы должны сформироваться автоматически
Vi2>Другой пример SomeFunc( *GetVar() ); при void SomeFunc( int& a ) и int* GetVar() { return NULL; }
Vi2>
Ну и что вы думали этим показать?
Это вообще к делу не относиться.
Это просто пример способа оптимизации кода, при чём то, что я написал?
Ну возвращаемое значение функции в eax. Так что вам собственно не нравиться!!!
Я сделал попытку теор определения: Указатель -- это переменная. Ссылка -- Не обязательно
Ваш пример только подтверждает. После того, как eax попадает в стек, он превращаеться в локальную переменную — значит указатель!!!
Проблемма в локальных переменных... потому что кажеться, что нет различий между указателем и функцией...
Однако разница всё таки громадна. И разработчики C++ знают о ней, и Страуструп знает, иначе никто никогда б не вводил их в C!
Но эту разницу нельзя увидить в коде. Она есть в способе создания кода компилятором.
Да это теории, теории, и теории.
Я просто попытался привести лёгкий и наглядный пример, чтобы у программиста сразу сформировалось впечатление.
Здравствуйте Vi2, Вы писали:
Vi2>>>>Я достаточно часто видел и такое описание функций Vi2>
Vi2>>>>void SomeFunc( int& a = *(int*)NULL );
Vi2>
Vi2>Но я бы не сказал, что это хороший стиль, что ему надо следовать. Даже наоборот так делать не надо. Однако такой код есть, и в достаточно известной фирме.
Vi2>Где-то в прошлых топиках было (http://www.rsdn.ru/forum/message.asp?mid=49650&only
и может другие есть), что такое выражение *(int*)NULL — неопределённое поведение.
"Неопределенное поведение" — термин языка С++. Означает, что стандарт языка не определяет, как именно должен вести себя компилятор при встрече с такими моментами, или, вернее, как должна себя вести программа с таким кодом. Но! Тот код, о котором ты говоришь, написан чисто для этого компилятора, чье поведение досконально известно.
Таким образом, с точки зрения чистого С++ этот код опасен, т.е. слабо переносим (но все-таки переносим в пределах как-минимум одного компилятора во всех его версиях), но высказывание Vi2>То, что код компилится VC6.0 и работает — это просто счастье (короткое?) разработчиков.
верно только для тех, кто не знает, что делает.
Этот код — Microsoft specific, короче.
Все, что написано Microsoft и для Microsoft (MFC, STL), написано для конкретного компилятора, и может опираться на его особенности реализации. Таким образом, код становится более платформно-зависимым, что совсем не значит, что плохим. Давно известно, что чем более общо сделана некоторая вещь, тем труднее ее использовать (примеров масса: от фукнкции qsort до MSWord).
Здравствуйте Vi2, Вы писали:
Y>>Я читал кажется у Элджера, что он советует использовать ссылки для лаконичности и простоты. А указатели использовать только тогда, когда передаваемый объект может не существовать (т.к. не может быть ссылки на NULL).
Vi2>Ещё как может! Vi2>Т.к. при Type* GetVar(); и возврате валидного NULL для неё в функцию SomeFunc( *GetVar() ) передастся пустая ссылка и без "вполне осознаннах телодвижений — такое по ошибке не сделаешь — в отличие от пустых и некорректных указателей" TepMuHyc. Это может быть и баг VC6.0, но тем не менее ...
Vi2>Так что и ссылками можно наворотить дай бог что.
Наворотить можно много чего. Речь идет о том, что не существует легального способа получить нулевую ссылку в С++. В твоем примере код '*GetVar()' порождает неопределенное поведение, т.к. производит разадресацию нулевого указателя. Таким образом, проверять ссылку на "равенство NULL" внутри такой функции бессмысленно — к этому моменту ошибка в программе уже произошла.
В этом и заключается одно из отличий ссылки от указателя: в случае со ссылкой, проверка на NULL — это задача именно формирующего ссылку кода. А использующий ссылку код такую проверку делать не должен — это не его обязанность. Именно это и имеется в виду в книгах по С++, когда говорится, что ссылка не может быть нулевой.
С указателем ситуация иная — формирующий код имеет полное право сформировать NULL-укзатель, и если использующему коду это важно, то он должен выполнить проверку на NULL самостоятельно.
Здравствуйте Kaa, Вы писали:
Kaa>Таким образом, с точки зрения чистого С++ этот код опасен, т.е. слабо переносим (но все-таки переносим в пределах как-минимум одного компилятора во всех его версиях), но высказывание
А откуда взялось "во всех его версиях"?
Best regards,
Андрей Тарасевич
Re[8]: Псевдо-экономия
От:
Аноним
Дата:
22.07.02 17:49
Оценка:
Здравствуйте Андрей Тарасевич, Вы писали:
АТ>А откуда взялось "во всех его версиях"?
Здравствуйте TepMuHyc, Вы писали:
TMH>Здравствуйте Алекс Пронскявичус, Вы писали:
АП>>в каких случаях следует предпочесть ссылки, TMH>- Везде где надо передать параметр "по ссылке" — например чтобы вернуть результат выполнения функции
Жестоко это... , таким образом ты запутаешь человека который твой код будет использовать.
int a = 5;
SomeFunc(a); // Поменяли "a", но мне об этом надо догадаться.
В этом случае надо параметры каждой используемой функции просматривать.
int a = 5;
SomeFunc(&a); // Поменяли a, но я возможность изменения "a" вижу сразу.
Z™>Жестоко это... :no: , таким образом ты запутаешь человека который твой код будет использовать.
Нисколечко. Все что надо — это внимательно читать RTFM :-)
Посмотри документацию по MFC, ATL, STL — как часто там используются указатели?
А ссылки?
Z™>int a = 5;
Z™>SomeFunc(a); // Поменяли "a", но мне об этом надо догадаться.
Теперь другой пример: SomeFunc(5); И что же нам скажет компилятор?
Я думаю выдаст трезвую и взвешенную оценку умственных способностей программиста.
Z™>В этом случае надо параметры каждой используемой функции просматривать.
Их в любом случае просматривать надо перед тем как вызов оной функции писать.
Хотя бы для того чтобы иметь представление что данная функция делает.
То есть, здесь лично я вижу две проблемы:
1) Человек имеет серьезные проблемы с языком С и C++. Т.к. ему трудно
распознать смысл конструкций TYPE* и TYPE&. Пусть учится. Или идет на...
на... На Джаву! :-)
2) Программист не желает читать документацию. В этом случае его умственные
способности а также обстоятельства его зачатия, рождения и воспитания будут
подробно рассмотрены в курилке другими программерами. Или же работодателем...
Z™>int a = 5;
Z™>SomeFunc(&a); // Поменяли a, но я возможность изменения "a" вижу сразу.
...Мне это напомнило набивший оскомину пример со scanf() из справочника Кернигана-Ричи.
Где новичку вбивают в голову "если вы используйте параметр для приема данных,
передавайте в функцию его адрес". С тех времен прошло уже достаточно времени
и были придуманы более удобные конструкции — классы, шаблоны, ах да, еще
и ссылки :-).
Теперь тот же пример немного по-другому: SomeFunc(0); — тоже вполне
корректно, а последствия, думаю, очевидны.
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Z™>int a = 5;
Z™>SomeFunc(a); // Поменяли "a", но мне об этом надо догадаться.
Z™>В этом случае надо параметры каждой используемой функции просматривать.
Z™>int a = 5;
Z™>SomeFunc(&a); // Поменяли a, но я возможность изменения "a" вижу сразу.
Я же использую оба варианта в зависимости от того, можно ли по имени SomeFunc определить, изменяет ли она параметр или нет. Если с первого взгляда кажется, что она не изменяет параметр, а на самом деле делает так, то лучше передать указатель. Если смысл ф-ии в изменении своего аргумента, то ради красивости можно передать и ссылку.
Когда есть возможность написать x = f(x), именно так и стоит делать. К сожалению так бывает не всегда.
На самом деле, все зависит от стиля, который ты выбрал. Если твой взгляд не царапает обилие укзателей, почему бы и нет.
Здравствуйте TepMuHyc, Вы писали:
TMH>Теперь другой пример: SomeFunc(5); И что же нам скажет компилятор? TMH>Я думаю выдаст трезвую и взвешенную оценку умственных способностей программиста.
В обоих случаях скажет, что не может привести один тип к другому.
TMH>2) Программист не желает читать документацию. В этом случае его умственные TMH>способности а также обстоятельства его зачатия, рождения и воспитания будут TMH>подробно рассмотрены в курилке другими программерами. Или же работодателем...
3) Документация отсутствует или она очень плоха.
Код, применяющий указатели проще для понимания. По-моему с этим спорить трудно. А если тебе по каким-то причинам не нравятся указатели, можешь переходить на Джаву! :-)
Здравствуйте Алекс Пронскявичус, Вы писали:
АП>Доброго времени суток. АП>Вот вопросец небольшой, в каких случаях следует предпочесть ссылки, а в каких указатели? АП>Я раньше где-то читал, что, где возможно, лучше использовать ссылки — красивше выглядит. Так вот набрёл на проблему, когда имеется указатель, а функция принимает ссылку:
АП>
АП>SomeFunc(Type& var)
АП>{
АП> // ...
АП>}
АП>
АП>Type* var= GetVar(); АП>SomeFunc(var) // ???
АП>Что выбрать? АП>1. Заменить параметр функции на указатель, а значит признать, что такая же ситуация может произойти и с другими ссылками. АП>2. Может есть возможность получить ссылку через указатель, не копируя память в другую переменную?
// В данном случае наиболее подходящий вариант вызвать ф-цию след. образом
SomeFunc (*var);
// В этом случае в качестве параметра будет передаваться по ссылке не указатель а
// обьект на который он указывает
Z™>>int a = 5;
Z™>>SomeFunc(a); // Поменяли "a", но мне об этом надо догадаться. TMH>Теперь другой пример: SomeFunc(5); И что же нам скажет компилятор? TMH>Я думаю выдаст трезвую и взвешенную оценку умственных способностей программиста.
Что то ты напутал... Он выдаст "cannot convert parameter 1 from 'int' to 'int*' и ничего больше. А это означает что ты уже обсуждаешь другую функцию (которую еще надо написать, если ты просто значение передаешь).
Z™>>В этом случае надо параметры каждой используемой функции просматривать. TMH>Их в любом случае просматривать надо перед тем как вызов оной функции писать. TMH>Хотя бы для того чтобы иметь представление что данная функция делает.
Я пишу не про написание вызова функции. Я пишу про сопровождение написанного кем то кода. Просматривая сотни строк кода, постоянное рытье в документации сильно утомляет, надо ведь каждую функцию подозревать в изменении переменных, если где то используется возврат по ссылке. Зачастую самые "умные" куски кода вообще не документируются.
Не надо смешивать сложность с запутанностью , надо писать так, что бы код поменьше напрягал мозги впоследствии.
TMH>Теперь тот же пример немного по-другому: SomeFunc(0); — тоже вполне TMH>корректно, а последствия, думаю, очевидны.
Неужели трудно хотя бы в дебаг версии проверять параметры функции ?
Мне встречались люди которые оправдывали свою лень и нежелание хотя бы вставлять ASSERTы, тем, что неправильное значение в функцию никогда не передается.
Книгу Ален И. Голуба. "Правила программирования" всем рекомендую, там все разжевано.
Зачастую что то (кого то) обсуждают в курилках владея искаженной или неполной информацией.
Я думаю, что можно обсуждать вопросы без проставления нулей, тем более что исходный ответ TepMuHyc не заслуживал такой оценки изначально — он никому не грубил, не хамил, не навязывал свою точку зрения и т.п. Если уж, уважаемый Zilog™, имеете претензии лично к TepMuHyc, то перейдите на личную переписку и выскажите там все.
А теперь по сути разборки.
То, что вы начали обсуждать — это дело вкуса, принятых в кампании правил и т.п., и тут уж каждый будет считать своё решение единственно правильным.
С++ даёт 4 возможности передачи параметра не по значению: ret_type func( type* ptr );
ret_type func( type& ref );
ret_type func( const type* cptr );
ret_type func( const type& cref );
Первые два дают право изменять параметр, вторые (при хорошем стиле) — нет. Всё! Обсуждать здесь даже нечего.
Вы имеете полное право и возможность пользоваться всеми этими способами без ограничения со стороны компилятора или со стороны стандарта, если учесть пропускаемую некоторыми компиляторами недопустимость ссылки на пустой объект.
Поэтому вы вольны выбирать по своему вкусу правила записи функций, изменяющей свои параметры, и требовать их выполнения в своей компании, и никто не должен осуждать вас за это.
Однако, было бы неплохо учитывать проверку компилятора при указании ключевого слова const и/или указывать информацию об изменении параметров в названии функции. Опять же это дело вкуса.
PS
Обычно советы использовать то-то и то-то носят характер рекомендаций.
Поэтому и нужно писАть: Как правило, ссылки используются ..., указатели используются ...
А требования использовать то-то и то-то носят характер обязанности.
Поэтому и нужно писАть: Вы должны использовать ссылки ..., а указатели — ...
Здравствуйте SergH и Зилог, Вы писали:
TMH>>2) Программист не желает читать документацию. В этом случае его умственные SH>3) Документация отсутствует или она очень плоха.
...Но заголовки-то есть ?
SH>Код, применяющий указатели проще для понимания.
Ты не забыл добавить ИМХО?
SH>По-моему с этим спорить трудно.
Напиши Страуструпу. Он довольно охотно отвечает на умные письма. И с ним даже можно поспорить.
А вот переспорить — трудновато...
...Это была преамбуда. Теперь амбула.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
То ли ссылки, то ли указатели — зависит от многого — legacy code, корпоративный/командный стиль кодирования, личные предпочтения и т.д. Короче — случаи бывают разные и вариантов много. И все они заслуживают уважения.
Лично я более склоняюсь к использованию ссылок — считаю это "хорошим тоном" в C++...
Теперь по "классам":
Указатели (плюсЫ)
1) C-only код весьма распространен — в особенности на Юнихах. Т.к. ссылок в С нету создается иллюзия что с одними указателями жизнь хороша.
2) Когда приходится писать DLL или Shared Library, то хороший тон экспортировать функции в соглашениях С — чтобы данная библиотека была двоично совместима с разными компиляторами.
3) При вызове функций в списке параметров явно виден оператор адресации.
Указатели (минусЫ)
1) Очень легко создать некорректный указатель "случайно" — достаточно забыть его проинициализировать.
2) Они весьма непонятны новичкам переползшим на C++ с Java/Basic/Pascal
3) При вызове функций в списке параметров явно виден оператор адресации. Но далеко не всегда. Для того чтобы _всегда_ корректно его пременять надо быть с адресной арифметикой более на "ты"...
Ссылки (плюсЫ)
1) Очень трудно создать некорректрую ссылку "случайно"
2) Паскалистам и Визуальным Басикерам ссылка как аргумент функции близка и понятна.
3) Ссылка как аргумент функции не нуждается в проверке ее корректности.
4) Страуструп рекомендует ее использовать взамен указателя.
Ссылки (минусЫ)
1) "Упертые сишники" ни в зуб ногой не понимают зачем она нужна.
2) см. Указатели (плюсЫ) пункт 2
2) см. Указатели (плюсЫ) пункт 3 и Указатели (минусЫ) пункт 3
Но ИМО нормальный C/C++ программер должен быть знаком и свободно оперировать обоими.
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте Zilog™, Вы писали:
Z>>>В этом случае надо параметры каждой используемой функции просматривать. TMH>>Их в любом случае просматривать надо перед тем как вызов оной функции писать.
Z™>Я пишу не про написание вызова функции. Я пишу про сопровождение написанного кем то кода.
Хрен редьки не слаще. Для того чтобы код сопровождать ты его должен ЗНАТЬ. В идеале — знать что делает КАЖДАЯ
функция. А следовательно просмотреть доку или код данной функции.
Z>Просматривая сотни строк кода, постоянное рытье в документации сильно утомляет
Позволь спросить, а за что тебе зарплату платят? За твои красивые глаза? Или за то что ты код сопровождаешь? Если же тебя так утомляет умственная нагрузка лучше переведись в животноводы...
Z>надо ведь каждую функцию подозревать в изменении переменных, если где то используется возврат по ссылке.
Сразу впоминается анекдот: "Слониху изнасиловали — всех подозревают"... Если уж подозреваешь не сочти за труд — поставь на этом вызове брекпойнт... или почитай по функции доку.
Z>Зачастую самые "умные" куски кода вообще не документируются.
Преблема ВАШЕЙ корпоративной культуры — это еще не глобальная проблема.
TMH>>Теперь тот же пример немного по-другому: SomeFunc(0); — тоже вполне TMH>>корректно, а последствия, думаю, очевидны. Z>Неужели трудно хотя бы в дебаг версии проверять параметры функции ?
Только вот заметь, что если аргумент — ссылка, то этого не надо делать ВООБЩЕ.
Z>Зачастую что то (кого то) обсуждают в курилках владея искаженной или неполной информацией.
А обсуждают-то своего товарища, а потом когда заваривается новый проект и набирается команда никто его в этой новой команде видеть не хочет... И это не фантазии — сам знаю одного такого — у него зарплата не меняется со дня поступления на работу — вот уже 4 года...
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Kaa>Все, что написано Microsoft и для Microsoft (MFC, STL), написано для конкретного компилятора, и может опираться на его особенности реализации. Таким образом, код становится более платформно-зависимым, что совсем не значит, что плохим. Давно известно, что чем более общо сделана некоторая вещь, тем труднее ее использовать (примеров масса: от фукнкции qsort до MSWord).
Не надо путать общность алгоритма и независимость от особенностей реализации. Сама Microsoft категорически НЕ рекомендует полагаться на особенности реализации и НЕ гарантирует их переход от версии к версии.