- •Определение и задание ссылок.
- •Правила для ссылок.
- •3. Передача параметров в Си.
- •Int main ()
- •4. Передача параметров по ссылке в Си.
- •Int main ()
- •5. Отличие ссылок от указателей.
- •6. Использование ссылок.
- •7. Чего нельзя делать со ссылками.
- •Примеры использования ссылок.
- •Отличие ссылок от указателей.
7. Чего нельзя делать со ссылками.
По сути ссылка — это указатель, который обязательно при создании ини-циализировать каким-то значением;
нельзя изменять после этого.
Например, вот так написать вообще нельзя:
int &x;
Ссылку обязательно инициализировать! Подойдёт любой из следующих способов:
int z;
int *pz = &z;
int &x1 = z;
int &x2 = *pz;
int &x3 = * (int*) malloc (sizeof (int)); /* но это извращение */
int &x4 = *new int; /* точно такое же извращение */
int &x5 = x1; /* можно инициализировать и через другую ссылку */
/* Следует понимать, что x3, x4 инициализированны динамически.
Память не будет освобождена автоматически, в отличии от других случаев.
Это нужно будет сделать вручную. */
Ссылку, как только что было сказано, нельзя заставить ссылаться на другой объект. Раз уж она начала на что-то ссылаться, то она будет на это ссылаться до конца жизни. Например, заведём указатель и ссылку и попытаемся их поменять:
int z = 3, zz = 5;
int *pz = &z; /* теперь pz указывает на z, *pz == z == 3 */
int &x = z; /* теперь x указывает на z, x == z == 3 */
pz = &zz; /* теперь pz указывает на zz, *pz == zz == 5 */
x = zz; /* но x ведь указывала на z! значит, мы написали «z = zz».
теперь x == z == zz == 5 */
Изменить указатель, скрытый за ссылкой x, нельзя — просто нет в Си++ опера-тора, позволяющего это сделать.
Ещё одно важное отличие — ссылка не может ссылаться «ни на что». То есть, если указатель может иметь значение NULL, то ссылка — нет.
Разве что мы специально постараемся...:
int& g = *(int*)(0);
int* h = &g; /* h == 0 */
g = 2; /* Ошибка выполнения */
Не бывает массивов ссылок и указателей на ссылки — такое не предусмотрено синтаксисом языка Си++.
Если ссылка есть поле класса, то класс обязан иметь явно написанный конст-руктор, и все такие поля обязаны быть инициализированы в ctor-нициализаторе:
int i;
class Ref
{
public:
Ref() : m_r(i) {}
private:
int& m_r;
};
Неконстантная ссылка на тип T может быть инициализирована только lvalue, и только типа T — запрещены даже простейшие преобразования типа short в long и Derived к Base.
Показанное ниже неверно:
Derived d;
Base& br = d; // требуется приведение
short i;
long& ri = i; // требуется приведение
long& lr = 1; // не есть lvalue
С константной ссылкой эти ограничения снимаются путем создания временного безымянного объекта, на который и будет ссылаться ссылка.
Derived d;
const Base& cbr = d; // работает
На деле это означает:
Derived d;
Base __tmp1 = d;
const Base& cbr = __tmp1;
Таким образом, ссылки нужны для изменения значения параметра;
Но даже если вам это не нужно, для большого класса или структуры передача по ссылке гораздо быстрее и экономит память.
void f(Monster x);
void g(const Monster& x);
...
Monster barmaley;
f(barmaley); // будет создаваться копия монстра barmaley, а после выполнения функции — уничтожаться.
g(barmaley); // Функция получит адрес barmaley. Kопию создавать не надо.
По этой причине параметры длинее 8 байтов почти всегда передаются по ссылке.
Ссылки и массивы.
Ссылки полезны и в циклах по элементам массива.
for (int i = 0; i < 10; i ++)
{
double& x = a[i];
if (x > 1)
x = 1;
if (x < 0)
x = 0;
x = x * x;
}
Во всех этих случаях можно было бы использовать указатель, и в Си так и делается, но, как сказано выше, указатели опасны. К тому же писать звёздочки и амперсанды — утомительно.
Но по-настоящему необходимы ссылки для перегрузки операторов, о которой пойдёт речь в следующих разделах. Так, например, что ++ обычно понимается как изменяющая свой аргумент, потому ::operator++(T) обычно обязана иметь ссылку в параметре — т.е. ::operator++(T&)
Точно так же operator[] обычно понимается как возвращающая lvalue, что требует T& Arr::operator[](int index);