Сообщение Re: Не могу понять ссылки в C++ от 15.06.2024 6:28
Изменено 15.06.2024 6:42 andrey.desman
Re: Не могу понять ссылки в C++
Здравствуйте, Worminator X, Вы писали:
WX>Из учебника Столярова так и не понял, что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
Ссылки — это не совсемсинтаксический сахар над указателями. По крайней мере не в смысле определения, хотя их и можно использовать как таковые. Ссылка — это альтернативное имя для какого-то выражения, алиас.
Саму ссылку нельзя изменить, она привязывается (bind) к чему-то в момент определения и остается привязанной к этому выражению до конца своего существования.
Грубо говоря, ссылка ведет себя как разыменованный константный указатель. Не указатель на константу, а именно константный указатель. А, например, константная ссылка как константный указатель на константу (const * const).
В твоем коде
создается ссылка current, которая привязывается к list. Она и останется привязанной к list.
Выражение current = ((IntegerList&)current).getNext() не перепривязывает ссылку current к другому объекту, а присваивает привязанному объекту list значение из list.getNext().
Если на указателях, то это эквивалентно следующему:
На ссылках такой цикл написать в принципе невозможно.
WX>По правилам хорошего тона в C++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками.
Нет такого правила. Всему свое применение.
WX>Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов):
Как раз здесь ссылки неуместны, если ты конечно не строишь иммутабельные списки. Если потребуется список поменять, его придется перестроить весь, от начала и до конца.
При этом, с динамической памятью на ссылках работать не получится.
Что касается управления памятью вручную, то да. Вместо обычных указателей здесь лучше использовать умный std::unique_ptr.
WX>И еще, почему, если в EmptyList сделать пустой конструктор, то на строки const EmptyList empty(); const IntegerList n3(3, empty); компилятор ругается? Вариант с const EmptyList empty(void) не помог.
const EmptyList empty(); — это объявление функции empty(), возвращающей const EmptyList. Смотри https://en.wikipedia.org/wiki/Most_vexing_parse
Исправить можно инициализатором фигурными скобками const EmptyList empty{};. Или вообще их отсутствием (все равно будет вызван конструктор по умолчанию).
WX>Из учебника Столярова так и не понял, что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
Ссылки — это не совсемсинтаксический сахар над указателями. По крайней мере не в смысле определения, хотя их и можно использовать как таковые. Ссылка — это альтернативное имя для какого-то выражения, алиас.
Саму ссылку нельзя изменить, она привязывается (bind) к чему-то в момент определения и остается привязанной к этому выражению до конца своего существования.
Грубо говоря, ссылка ведет себя как разыменованный константный указатель. Не указатель на константу, а именно константный указатель. А, например, константная ссылка как константный указатель на константу (const * const).
// ссылка
int a[5][5];
int& b = a[1][1];
b = 10; // эквивалентно a[1][1] = 10
b = a[0][0]; // эквивалентно a[1][1] = a[0][0], перепривязки здесь нет
b = 12; // снова эквивалентно a[1][1] = 12
// аналогично на указателях
int a[5][5];
int* const b = &a[1][1];
*b = 10; // эквивалентно a[1][1] = 10
*b = a[0][0]; // эквивалентно a[1][1] = a[0][0], перепривязки здесь нет
*b = 12; // снова эквивалентно a[1][1] = 12
// b = &a[0][0]; // нельзя, сам указатель b константный, его не перепривязать, как и ссылку
// константная ссылка
int a[5][5];
const int& b = a[1][1];
a[0][0] = b; // эквивалентно a[0][0] = a[1][1]
// b = ...; // нельзя
// аналогично на указателях
int a[5][5];
int const* const b = &a[1][1];
a[0][0] = *b; // эквивалентно a[0][0] = a[1][1]
// *b = ...; // нельзя
// b = ...; // нельзя
В твоем коде
for (
List ¤t = (List&)list;
current.getType() == LT_INTEGER_LIST;
current = ((IntegerList&)current).getNext()
)
создается ссылка current, которая привязывается к list. Она и останется привязанной к list.
Выражение current = ((IntegerList&)current).getNext() не перепривязывает ссылку current к другому объекту, а присваивает привязанному объекту list значение из list.getNext().
Если на указателях, то это эквивалентно следующему:
for (
List* const current = &(List&)list; // сам указатель константный, его нельзя поменять, как и привязку ссылки
current->getType() == LT_INTEGER_LIST;
*current = *((IntegerList*)current)->getNext() // считаем, что getNext() тоже указатель возвращает, как во втором варианте
)
На ссылках такой цикл написать в принципе невозможно.
WX>По правилам хорошего тона в C++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками.
Нет такого правила. Всему свое применение.
WX>Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов):
Как раз здесь ссылки неуместны, если ты конечно не строишь иммутабельные списки. Если потребуется список поменять, его придется перестроить весь, от начала и до конца.
При этом, с динамической памятью на ссылках работать не получится.
Что касается управления памятью вручную, то да. Вместо обычных указателей здесь лучше использовать умный std::unique_ptr.
WX>И еще, почему, если в EmptyList сделать пустой конструктор, то на строки const EmptyList empty(); const IntegerList n3(3, empty); компилятор ругается? Вариант с const EmptyList empty(void) не помог.
const EmptyList empty(); — это объявление функции empty(), возвращающей const EmptyList. Смотри https://en.wikipedia.org/wiki/Most_vexing_parse
Исправить можно инициализатором фигурными скобками const EmptyList empty{};. Или вообще их отсутствием (все равно будет вызван конструктор по умолчанию).
Re: Не могу понять ссылки в C++
Здравствуйте, Worminator X, Вы писали:
WX>Из учебника Столярова так и не понял, что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
Ссылки — это не совсемсинтаксический сахар над указателями. По крайней мере не в смысле определения, хотя их и можно использовать как таковые. Ссылка — это альтернативное имя для какого-то выражения, алиас.
Саму ссылку нельзя изменить, она привязывается (bind) к чему-то в момент определения и остается привязанной к этому выражению до конца своего существования.
Грубо говоря, ссылка ведет себя как разыменованный константный указатель. Не указатель на константу, а именно константный указатель. А, например, константная ссылка как константный указатель на константу (const * const).
В твоем коде
создается ссылка current, которая привязывается к list. Она и останется привязанной к list.
Выражение current = ((IntegerList&)current).getNext() не перепривязывает ссылку current к другому объекту, а присваивает привязанному объекту list значение из list.getNext().
Если на указателях, то это эквивалентно следующему:
На ссылках такой цикл написать в принципе невозможно.
WX>По правилам хорошего тона в C++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками.
Нет такого правила. Всему свое применение.
WX>Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов):
Как раз здесь ссылки неуместны, если ты конечно не строишь иммутабельные списки. Если потребуется список поменять, его придется перестроить весь, от начала и до конца.
При этом, с динамической памятью на ссылках работать не получится.
Что касается управления памятью вручную, то да. Вместо обычных указателей здесь лучше использовать умный std::unique_ptr.
WX>И еще, почему, если в EmptyList сделать пустой конструктор, то на строки const EmptyList empty(); const IntegerList n3(3, empty); компилятор ругается? Вариант с const EmptyList empty(void) не помог.
const EmptyList empty(); — это объявление функции empty(), возвращающей const EmptyList. Смотри https://en.wikipedia.org/wiki/Most_vexing_parse
Исправить можно инициализатором фигурными скобками const EmptyList empty{};. Или вообще их отсутствием (все равно будет вызван конструктор по умолчанию).
WX>Из учебника Столярова так и не понял, что такое есть ссылка в C++ (вроде синтаксический сахар над указателем, но как-то странно и непонятно работает).
Ссылки — это не совсемсинтаксический сахар над указателями. По крайней мере не в смысле определения, хотя их и можно использовать как таковые. Ссылка — это альтернативное имя для какого-то выражения, алиас.
Саму ссылку нельзя изменить, она привязывается (bind) к чему-то в момент определения и остается привязанной к этому выражению до конца своего существования.
Грубо говоря, ссылка ведет себя как разыменованный константный указатель. Не указатель на константу, а именно константный указатель. А, например, константная ссылка как константный указатель на константу (const * const).
// ссылка
int a[5][5];
int& b = a[1][1];
b = 10; // эквивалентно a[1][1] = 10
b = a[0][0]; // эквивалентно a[1][1] = a[0][0], перепривязки здесь нет
b = 12; // снова эквивалентно a[1][1] = 12
// аналогично на указателях
int a[5][5];
int* const b = &a[1][1];
*b = 10; // эквивалентно a[1][1] = 10
*b = a[0][0]; // эквивалентно a[1][1] = a[0][0], перепривязки здесь нет
*b = 12; // снова эквивалентно a[1][1] = 12
// b = &a[0][0]; // нельзя, сам указатель b константный, его не перепривязать, как и ссылку
// константная ссылка
int a[5][5];
const int& b = a[1][1];
a[0][0] = b; // эквивалентно a[0][0] = a[1][1]
// b = ...; // нельзя
// аналогично на указателях
int a[5][5];
int const* const b = &a[1][1];
a[0][0] = *b; // эквивалентно a[0][0] = a[1][1]
// *b = ...; // нельзя
// b = ...; // нельзя
В твоем коде
for (
List ¤t = (List&)list;
current.getType() == LT_INTEGER_LIST;
current = ((IntegerList&)current).getNext()
)
создается ссылка current, которая привязывается к list. Она и останется привязанной к list.
Выражение current = ((IntegerList&)current).getNext() не перепривязывает ссылку current к другому объекту, а присваивает привязанному объекту list значение из list.getNext().
Если на указателях, то это эквивалентно следующему:
for (
List* const current = &(List&)list; // сам указатель константный, его нельзя поменять, как и привязку ссылки
current->getType() == LT_INTEGER_LIST;
*current = *((IntegerList*)current)->getNext() // считаем, что getNext() тоже указатель возвращает, как во втором варианте
)
На ссылках такой цикл написать в принципе невозможно.
WX>По правилам хорошего тона в C++ нужно всегда использовать ссылки вместо указателей (кроме интеграции с кодом на Си) и по возможности не управлять памятью вручную, т.к. это чревато утечками.
Нет такого правила. Всему свое применение.
WX>Я попытался переписать код, и вышло примерно следующее (для упрощения пока без шаблонов):
Как раз здесь ссылки неуместны, если ты конечно не строишь иммутабельные списки. Если потребуется список поменять, его придется перестроить весь, от начала и до конца.
При этом, с динамической памятью на ссылках работать не получится.
Что касается управления памятью вручную, то да. Вместо обычных указателей здесь лучше использовать умный std::unique_ptr.
WX>И еще, почему, если в EmptyList сделать пустой конструктор, то на строки const EmptyList empty(); const IntegerList n3(3, empty); компилятор ругается? Вариант с const EmptyList empty(void) не помог.
const EmptyList empty(); — это объявление функции empty(), возвращающей const EmptyList. Смотри https://en.wikipedia.org/wiki/Most_vexing_parse
Исправить можно инициализатором фигурными скобками const EmptyList empty{};. Или вообще их отсутствием (все равно будет вызван конструктор по умолчанию).