Константные ссылки и указатели

Задания раздела 2.4.2
2.27. Какие из следующих инициализаций допустимы? Объясните почему.
(a) int i=-1, &r=0;
(b) int* const p2=&i2;
(c) const int i=-1, &r=0;
(d) const int* const p3=&i2;
(e) const int *p1=&i2;
(f) const int &const r2;
(g) const int i2=i, &r2;
2.28. Объясните следующие определения. Какие из них недопустимы?
(a) int i, *const cp;
(b) int *p1, *const p2;
(c) const int ic, &r=ic;
(d) const int *const p3;
(e) const int *p;
2.29.
С учетом переменных из предыдущих упражнений, какие из следующих присвоений допустимы? Объясните почему:
(a) i=ic;
(b) p1=p3;
(c) p1=⁣
(d) p3=⁣
(e) p2=p1;
(f) ic=*p3;


p101_ex2-4-2

Вот и началось веселье. Понять всё это не трудно, просто нужна концентрация. Как только закончу тему 2.4 начну ковырять свой vds сервер. пустой (только с предустановленным debian 8 minimal) и без панели управления. Чувствую будет много боли, унижений и бессонных ночей. Потому что я точно такой же администратор удаленных серверов, как и программист. Зачем мне это? Хочу свой teamspeak server, который на виртуальный хостинг никак не поставить. Ну да так вот.
Ссылку на константу можно определить. Её нельзя использовать для изменения объекта, с которым она связана. Так как объект по ссылке нельзя изменить, то сама ссылка должна быть константной.
const int i=1024;
const int &a=i;
int &r=i; // ошибка, ссылка на константу должна быть константной
Но по факту никаких константных ссылок не бывает. Потому что ссылка, однажды инициализированная, не может указывать на другой объект. const int &a - ссылка на константу, а const указывает на то, что значение по ссылке изменить нельзя. Константный или неконстантный тип указывает на то, что при помощи ссылки можно сделать.
И тут началось вот что. Исключение из правил.
Можно инициализировать ссылку на константу результатом выражения, тип которого может быть преобразован в тип ссылки. Можно связать ссылку на константу с неконстантным объектом, литералом  или более общим выражением. Пример:
#include <iostream>
int main ()
{
int i=42;
const int &r1=i;
const int &r2=42;
std::cout << "r2=" << r2 <<std::endl;
const int &r3=r1*2;
std::cout << "i="<< i << " r1=" << r1 << " r3=" << r3 <<std::endl;
int &r4=r1*2; // ошибка, r4 простая, неконстантная ссылка
return(0);
}
Убрав строчку, в которой есть ошибка, получим вывод, над которым стоит задуматься:
p98_ex_middlrez
Кто бы мог подумать, что программа создаёт временную переменную. Хотя вроде бы всё логично, надо же где-то хранить результат вычисления. Надо смотреть адреса, чтобы понять где здесь что.

Получилась вот такая программа:

#include <iostream>
int main ()
{
int i=1024;
const int &r1=i; int *p1=&i;

const int &r2=r1; const int *p2=&r2;

const int &r3=r1*2; const int *p3=&r3;

std::cout << "pointer to i " << p1 << " " << *p1 << std::endl;
std::cout << "pointer to r2 " << p2 << " " << *p2 << std::endl;
std::cout << "pointer to r3 "<< p3 << " " << *p3 <<std::endl;

return(0);
}
p98_ex_middlrez_1
В этой программе видно, как происходит магия. Ссылка r3 на выражение уже ведет в другое место, как будто бы это литерал. Или временная переменная.
Вот еще такая программа. Она тоже классная:

#include <iostream>
int main ()
{
double i=1024.24;
const int &r=i;
const double *p=&i;
const int *p1=&r;

std::cout << i << " " << r <<std::endl;
std::cout << p << " " << p1 <<std::endl;

return(0);
}
в ней так же создаётся временная переменная и адрес показывает другой.
p98_ex_middlrez_2
Константная ссылка показывает что с её помощью нельзя изменить объект. В то же время если объект не константа то его можно изменить другим способом.
Константные указатели.
Так же как и простые ссылки, простые указатели не могут хранить адрес константного объекта. Для этого существуют константные указатели.
const int i=1024;
const int *p=&i;
int *p1=&i; // ошибка
Точно так же, несовпадение типов допускается в случае константных указателей. Но дело в том, что я не смог повторить опыт из книжки.
p100_mist
Во-первых, сам пример из книжки некорректен.
Во-вторых это не работает у меня. Не знаю почему пока что. Я точно что-то делаю не так, но пока что знаний исправить не хватает.
#include <iostream>
int main ()
{
double dval=1024.24;
const int *cptr=&dval;
std::cout << dval << " " << cptr <<std::endl;
return(0);
}
Только если я поменяю тип указателя на double (const double* cptr) программа начинает компилироваться.

Следует различать записи константного указателя и константного указателя на константный объект.
int i=1024;
int *const p=&i;
const double pi=3.14;
const double* const r=&pi;

#include <iostream>
int main ()
{
int i=1024;
int *const p=&i;
const double pi=3.14;
const double* const r=&pi;
std::cout << p << " " << r <<std::endl;
return(0);
}
Если указатель константный, но указывает на неконстантный объект, то его можно использовать для изменения значения объекта, на который он указывает:
#include <iostream>
int main ()
{
int i=1024;
int *const p=&i;
*p=24;
std::cout << p << " " << i <<std::endl;
return(0);
}
Указатель на константу - const int* p=&i; - в этом случае i должен быть константой. Использовать p для изменения значения i нельзя.
Константный указатель - int* const p=&i; - а в этом случае i может быть неконстантной. Использовать p для изменения значения i можно.
Константный указатель на константу - const int* const p=&i; - здесь всё  - константы. Использовать p для изменения значения i нельзя.
#include <iostream>
int main ()
{
int i=1000;
int* const a=&i;
const int i2=1024;
const int* const p=&i2;
std::cout << a << " " << i <<std::endl;
std::cout << p << " " << i2 <<std::endl;
return(0);
}
2.27. Какие из следующих инициализаций допустимы? Объясните почему.
(a) int i=-1, &r=0;
(b) int* const p2=&i2;
(c) const int i=-1, &r=0;
(d) const int* const p3=&i2;
(e) const int *p1=&i2;
(f) const int &const r2;
(g) const int i2=i, &r2;

(a) int i=-1, &r=0; // ошибка при инициализация ссылки
(b) int* const p2=&i2; // допустимо
(c) const int i=-1, &r=0; // допустимо, ссылка r ссылается на временный объект
(d) const int* const p3=&i2; /* допустимо, константный указатель на константный объект */
(e) const int *p1=&i2; // допустимо, указатель на константу
(f) const int &const r2; /*  недопустимо, хотя бы потому что ссылка неинициализрована, так же запись неправильная и константной ссылки на константу не бывает (наверное) */
(g) const int i2=i, &r2; // ошибка, ссылка r2 неинициализирована
2.28. Объясните следующие определения. Какие из них недопустимы?
(a) int i, *const cp;
(b) int *p1, *const p2;
(c) const int ic, &r=ic;
(d) const int *const p3;
(e) const int *p;

(a) int i, *const cp; // недопустимо, константный указатель неинициализирован
(b) int *p1, *const p2; // недопустимо, константный указатель неинициализирован
(c) const int ic, &r=ic; // недопустимо, константа неинициализирована
(d) const int *const p3; /* недопустимо, константый указатель на константный объект неинициализирован */
(e) const int *p; // допустимо, указатель на константу
2.29. С учетом переменных из предыдущих упражнений, какие из следующих присвоений допустимы? Объясните почему:
(a) i=ic;
(b) p1=p3;
(c) p1=&ic;
(d) p3=&ic;
(e) p2=p1;
(f) ic=*p3;

Ну что за фигня. Какие переменные, из каких упражнений? Что за загадки на ровном месте? Это так трудно было вписать в задание переменные? Достали.
Константы менять нельзя. Всё, точка.

Я еще много раз перечитаю эту тему и, возможно, буду что-то исправлять. Однозначно это пока что самое сложное для понимания чтиво, что я видел за последнее время. Вернее само по себе это не сложно. Просто написано так, чтобы никто не понял. Упрощу этот текст со временем, когда вникну поглубже.

Добавить комментарий