Ссылки С++
- Войдите на сайт для отправки комментариев
Пт, 25/01/2019 - 21:02
Есть такая тема, решил освежевать в памяти подзабытый прекрасный С++, пошёл читать учебник и наткнулся на главу "ссылки", где расписывают, как прекрасно, что вызываемая функция может менять переменные вызывающей функции по ссылке. Внутренне содрогнулся. Это действительно прекрасно, или это уже забытая фича С++, или я просто уже не секу?
не секете. Ссылки это еще широко используемый инструмент Си, как бы не хуже указателей. Как вам такая штука func()=5;
#190
Ссылки это еще широко используемый инструмент Си
Одна только беда - нету в Си никаких ссылок.
А
Schwarz78, можно конкретизировать вопрос?
Вас интересует наше мнение о ссылках и их полезности? Или что в них такого, чего нельзя было сделать указателями и для чего их ввели? Или какого ответа Вы ждёте?
Именно. Я сишник, и соответственно думаю указателями. Мой вопрос в том: зачем нужны ссылки? Прототип объявляется со ссылками, а я то передаю аргументы без ссылок (как я думаю - по значению), может я прототип не видел, или забыл как он выглядит. В чём сила ссылок? Или лучше ими вообще не зооупотреблять?
И, конечно, именно ваше мнение, ЕвгенийП.
Указатели я тож люблю, на функции меньше, но где здесь ссылки?
Указатели я тож люблю, на функции меньше, но где здесь ссылки?
Извините, Пух. Да. Ссылка. Можете объяснить, почему она необходима в здесь? А главное - что она делает? Красиво написано - не всегда красиво реализовано. Но чаще именно так. Объясните мне?
Самое сложное в ссылке это понять что такое ссылка , а что ссылкой не является. Сама по себе ссылка бонуса не приносит. Фишка в том что она имеет силу в функциях, как возврат по ссылке, так и параметры внутри ссылки. И самое главное экономит память. То есть по факту использует объекты которые есть, а не делает дубликаты внутри функций. Указатели вроде тоже, но они жрут память на адресс указателя.
Ну, «нужны» – понятие относительно, всё можно сделать и без них (как и без почти всего остального – на машине Тьюринга) вопрос в удобстве. В целом ссылка – это более высокоуровневый объект (гораздо дальше от железа), чем указатель.
Будучи один раз проинициализирована, она может быть использована вместо самого объекта, на который ссылается, как другой способ добраться до него. Если не говорить о несколько экзотических rvalue ссылках, то ссылка – lvalue, т.е. её можно использовать слева от операции присваивания. Любая операция с переменной-ссылкой (кроме её инициализации) – на самом деле операция с объектом, на который она ссылается. С нею самой нельзя сделать ничего. Например, если переменная-ссылка стоит в левой части присваивания, то значение будет присвоено объекту, на который она ссылается (а вовсе не ей самой).
Ссылка не может не быть инициализирована. Во время выполнения ссылка всегда ссылается на существующий объект правильного типа и т.д. (в отличие от указателя, который может указывать куда угодно и на что угодно). Всегда означает «всегда». Не бывает null-ссылки, например. Это сильно повышает безопасность работы с полученными функцией аргументами. Указатель ведь может быть nullptr, и это ещё полбеды, а может вообще, куда попало смотреть
Ссылку нельзя изменять. Если уж она проинициализирована каким-то объектом, то она будет ссылаться на него до конца своей жизни. В принципе, тут есть засада, можно сделать так, чтобы время жизни объекта, на который ссылается ссылка, было меньше времени жизни ссылки, и тогда она будет ссылаться на несуществующий объект (после его уничтожения), но это искусственная конструкция. Такое можно сделать специально, но я не видел ситуации, в которой можно было бы так ошибиться неумышленно. Стало быть, не надо заботиться о её (ссылки) волатильности, например (впрочем, и объявить её волатильной нельзя).
Ссылка не имеет адреса. К ней неприменима операция &, и она в принципе (на усмотрение компилятора) имеет право вообще не занимать память, т.е. физически не существовать. Это развязывает руки оптимизаторам. Кроме того, это даёт гарантии, что ссылка не будет изменена по указателю на неё, т.к. указателей на неё не бывает. Также не бывает ссылок на ссылки. У ссылок нет адреса.
У ссылок есть механизм «схлопывания» (collapsing). Немного упрощая это означает, что если, например, в typedef Вы определите новый тип NT через тип Т, а потом попытаетесь сделать ссылку на NT – в реальности получится ссылка на T.
Я говорил только о т.н. lvalue ссылках. Бывают ещё и rvalue ссылки, но разговор о них уведёт нас далеко – они придуманы для очень специфических вещей типа «семантики перемещения», давайте не будем.
Для чего же их вводили?
Ну, о безопасности (по сравнению с указателями) мы уже говорили.
Кроме того, в С++ (в отличие от С) существуют объекты у которых могут быть конструкторы, деструкторы, перегруженные операторы (никто не мешает написать собственный + или – или преобразование к какому-нибудь типу, взятие индекса и т.п.). Доступ к объекту по указателю иногда создаёт проблемы для отработки всех этих вещей (особенно если используется утиная типизация), доступ же по ссылке проблем не создаёт, т.к. операция над ссылкой – суть операция над самим объектом и, стало быть, все его механизмы отработают штатно.
Ну, вот, не знаю, сумбурно как-то получилось.
P.S.
И да, часто приходится читать - что ссылка - это адрес, так вот НЕТ. Категорически нет. Это не адрес (хотя dynehtyyt jyf может быть реальзована череp адрес, но это внутрення кухня). Но семантически это НЕ адрес. Тут нет адресной арифметики, нет "массивности" и т.п. - ссылка, это "ссылка на объект, которую можно использовать везде, где можно использовать объект" и не более того. Это боле высокий уровень абстракции, чем адрес. Будь она адресом, кто бы ей мешал иметь собственный адрес и адресную арифметику? Или из вредности убрали? Просто семантически ссылка адресом не является.
Например, в определённых случаях, компилятор имеет право заменить ссылочные параметры на передачу по значению (если посчитает. что в данном случае это эквивалентно и более эффективно), т.е. внутренне в фнуцию может пойти вовсе не адрес, а значение аргумента. Программист этого не знает и ему не нужно знать. Ссылка обеспечивает доступ к объекту - она обеспечила, а уж адрес там или ещё чего - это более высокий уровень абстракции.
ЕвгенийП, это как вы ссылку воспринимаете. Мне до такой формализации ещё ехать и ехать. Хотя я уже понял, что лучше не трогать то, что не понял. Да и медведь, вроде по-русски пишет, а тоже хрен поймёшь. Единственный выход - пытаться кодить и смотреть ассембелер, как в децве)
Единственный выход - пытаться кодить и смотреть ассембелер, как в децве)
Не выйдет, почитайте мой последний абзац. Может и так и эдак вылезти :)
Это да, компиляторы очень умные стали. То, что умнее меня, я понял 20 лет назад, и перешёл на С. Теперь вот о более умных компиляторах задумался. Но вижу, что рано вопросы задаю. Надо самому покодить для начала.
"Ну, о безопасности (по сравнению с указателями) мы уже говорили."
Разве? Когда и где? Меня этот аспект вообще интересует, а в плане ссылок - особенно, уж очень они опасные на первый взгляд.
Ссылка - это вот так func(type something&) или речь о чём-то другом?
В общем, "пока мне это не понадобилось, я не стану об этом думать". А когда понадобится - я сам пойму зачем это нужно.
Да, int foo( int& x, int& y ), и вызываем if( foo( x, y ) ) {}. А х и y - мои переменные. А foo может их легко поменять.
"Кроме того, в С++ (в отличие от С) существуют объекты у которых могут быть конструкторы, деструкторы, перегруженные операторы (никто не мешает написать собственный + или – или преобразование к какому-нибудь типу, взятие индекса и т.п.). Доступ к объекту по указателю иногда создаёт проблемы для отработки всех этих вещей (особенно если используется утиная типизация), доступ же по ссылке проблем не создаёт, т.к. операция над ссылкой – суть операция над самим объектом и, стало быть, все его механизмы отработают штатно."
Вот, уже начало укладываться в голове зачем оно нужно, спасибо.
Меня смущает то, что компилятор может сам по-себе заменить ссылку значением. Не бывает такого, что внутри функции *param = 10 (условно) не сработает и из функции ничего не вернется?
https://www.youtube.com/watch?v=hZOX-LlUETE Особенно в конце.
https://www.youtube.com/watch?v=diXxOaEef9o
https://www.youtube.com/watch?v=7btUwxD4V5s
Меня тоже смущают компиляторы подчас, что уж лукавить, но я это отношу к своим косякам, или просто ставлю оптимизацию на уровень меньше. На всякого мудреца довольно простоты.
"Ну, о безопасности (по сравнению с указателями) мы уже говорили."
Разве? Когда и где?
Извините, Пух, но смотреть видео - это слишком долго, а жизнь и так мимолётна. Лучше читать книги.
Да, точно. Я может быть тогда стану совсем наглым, и попрошу небольшой скетч, где ссылка будет категорически востребована. Если такое возможно.
лучше один раз увидеть, чем 100 раз прочитать и потом еще и не понять прочитаное.Если бы поняли что прочитали, то и этой темы не было.
Так я и спросил, потому что не понял. Вы уверены, что все понимают всё про ссылки? Я точно знаю, что личный опыт гораздо круче чужого, но чужой опыт - ещё круче, когда осознанный.
Меня смущает то, что компилятор может сам по-себе заменить ссылку значением. Не бывает такого, что внутри функции *param = 10 (условно) не сработает и из функции ничего не вернется?
Нет, не может. Алгоритмы обнаружения побочных эффектов давно известны и в этом ошибок не бывает. Если есть такая конструкция, то он не будет заменять ссылку значением.
ПС: Тот канал все же регулярно смотрите. Это сэкономит ваше время.
.
Да, int foo( int& x, int& y ), и вызываем if( foo( x, y ) ) {}. А х и y - мои переменные. А foo может их легко поменять.
А кто или что не даёт написать const перед тем(и) параметром, который менять нельзя? Тогда на попытку поменять компилятор обругается. Собственно и с указателями такой же приём работает.
Вот смотрите. Я никогда не был программистом. Мне пришлось. Я не собирался изучать Си. Мне пришлось. Теперь я просто увидел ардуино, а тут авр гцц. И вот я вижу людей, близких мне по духу, но ушедших далеко вперёд. Могу я спросить?
Да, я уже понял, что компилятору надо намекать. Но намекните всё же, зачем вы используете ссылки каждый день, или вы их нет?
Это видимо просто объяснить. Я знаю язык даже не с99, а наверное предыдущий, когда уже аргументы перестали определять перед фигурной скобкой. Этож контроллеры)
Да, я уже понял, что компилятору надо намекать. Но намекните всё же, зачем вы используете ссылки каждый день, или вы их нет?
Как я вас понимаю. Я тоже надуваюсь, как только кому-то интересно то, что знаю я.
намекните всё же, зачем вы используете ссылки каждый день
Ну, как зачем, ну вот надо мне передать в фунцкию сложный объект. Как передавать?
1. По значению - это значит создавать его копию, проводить всю инициализацию и т.п. Посмотрите вот эту тему, особенно, начинаю с заголовка "Неоправданная передача параметра функции по значению"
2. Передавать указатель. Ну, во-первых, нарываемся на громоздкий синтаксис разыменования, а во-вторых, теряем всякую защиту
3. По ссылке и полная защита, и никаких синтаксических наворотов.
Вот, потому и используем.
Указатель я передаю, когда мне надо работать с массивом. А когда с единичным объектом - зачем?
Я знаю язык даже не с99, а наверное предыдущий, когда уже аргументы перестали определять перед фигурной скобко
о сколько вам открытий чудных...
я тоже с 90-х знал тот еще с++ и какое же было мое откровение когда мне посоветовали и я стал изучать с++11 (стандарт 2011 года и все что было перед ним с начала 200х)
сейчас с++11 это самый доступный во многих компиляторах стандарт, хотя на той же ардуине где avr-g++ уже в значительной мере можно использовать с++17 и на подходе следующие стандарты
особенно в восторге от шаблонов, прощай препроцессор с его деревянной логикой
и кстати многое из того, что раньше решалось ссылками или указателями с шаблонами позволяет вообще без них обойтись (код в итоге может быть на машинном языке примерно тот же, но семантически ссылки не нужны)
много где там где конфигурация известна во время компиляции использование ссылок и указателей заменяется на использование шаблонов. для микроконтроллеров это во многих случаях позволят описывать классы со статическими данными и методами избегая вообще такого понятия как экземпляр класса при том, что мы сохраняем полностью все плюсы объектного программирования.
Ну, собственно 11-ый - наше фсё. Там были последние крупные, революционные изменения (та же "семантика перемещения"). В 14-ом и 17-ом - так по мелочи. Хотя, ходят гнусные слухи, что к 20-му готовятся новые большие перемены в языке, причём такие, которые мне (и не мне одному) совсем не нравятся.
constexpr так понимаю это ++14, весьма полезна, что то еще
enum class удобна, и хотя вроде это с++11 почему то в IAR его нет
constexpr так понимаю это ++14, весьма полезна, что то еще
Там она недоделанная. В 17-ом её довели до ума дополнив к if. Получилась "
if
constexpr
" которая действительно полезна.Простите, не понял, я Вас чем-то обидел, или на какой-то вопрос не ответил?
Конечно же нет.
Огромное спасибо всем, я не рассчитывал получить настолько развёрнутые ответы. Теперь, когда я вдруг пойму, что не могу дальше жить без ссылок, я всегда смогу прчитать эту тему, которая лучше любого учебника.