Ссылки (references). Ссылки на переменные, структуры, объединения и объекты классов. Отличия между ссылками и указателями. Примеры

Ссылки (references). Ссылки на переменные, структуры, объединения и объекты классов. Отличия между ссылками и указателями. Примеры


Содержание



1. Как объявить ссылку на переменную? Общая форма. Символ &

Во время объявления, ссылка должна обязательно инициализироваться. В простейшем случае ссылки объявляется следующим образом:

type & ref_var = var;

где

  • type – тип, на который объявляется ссылка;
  • ref_var – переменная типа «ссылка» на type;
  • var – переменная типа type, на которую объявляется ссылка.

2. Примеры объявления и использования ссылки на переменную, структуру, объединение

Пример 1. Объявление ссылки с именем rd на переменную целого типа d.

int d; // переменная
int & rd = d; // ссылка с именем rd на переменную d, инициализируется при объявлении

d = 0;
rd = 2; // d = 2

/*
int & rd2; // ошибка (ссылка должна быть инициализирована при объявлении)
*/

Пример 2. Объявление ссылки на структурную переменную.

Пусть задан тип структуры, которая описывает дату:

struct Date
{
    int day;
    int month;
    int year;
};

Тогда использование ссылки на структурную переменную типа Date может быть таким:

Date d;
Date & rd = d; // ссылка на структуру Date

// доступ к полям структуры по ссылке
rd.day = 14; // d.day = 14
rd.month = 1; // d.month = 1
rd.year = 1972; // d.year = 1972

Пример 3. Объявление и использование ссылки на объединение

Пусть задано объединение TYPES:

union TYPES
{
    int d[2];
    char c[4];
    float x;
};

Демонстрация использования ссылки на об¢единение TYPES:

TYPES t; // t - переменная типа "объединение"
TYPES & rt = t; // ссылка на t
char c;
int d;

rt.c[0] = 'A';
c = t.c[0];

rt.d[1] = 28;
d = t.d[1];

3. Какое отличие между ссылкой и указателем?

Между ссылкой и указателем можно выделить следующие основные отличия:

  • при объявлении ссылка должна быть обязательно инициализирована. Указатель не обязательно инициализировать при объявлении. Указателю можно присваивать значения и после его объявления в процессе выполнения программы;
  • после объявления ссылки и ее инициализации, этой ссылке нельзя присваивать адреса других объектов. Указатель может изменять свои значения сколько угодно;
  • указателю можно присваивать нулевое значение при объявлении. Ссылке нельзя присваивать нулевое значение.

4. Как объявить ссылку на объект некоторого класса? Пример объявления и использования

Пусть задан класс CFloat. В классе объявляется внутренняя переменная типа float, конструкторы и методы доступа Get(), Set().

Пример демонстрирует использование ссылки на объект класса CFloat.

Программный код модуля, созданного по шаблону Win32 Console Application демонстрирующий использование ссылки на объект класса, имеет вид:

#include "stdafx.h"
#include <iostream>
using namespace std;

class CFloat
{
    float x;

    public:
    // конструкторы класса
    CFloat() { x = 0; }
    CFloat(float nx) { x = nx; }

    // методы доступа
    float Get() { return x; }
    void Set(float nx) { x = nx; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    // ссылка на объект класса CFloat
    CFloat f; // f - объект класса CFloat
    CFloat & rf = f; // rf - ссылка на объект класса CFloat
    float x;

    rf.Set(5.5); // доступ к методам класса по ссылке
    x = f.Get(); // x = 5.5

    f.Set(-8.2);
    x = rf.Get(); // x = -8.2

    return 0;
}

5. Можно ли объявить ссылку на тип void? Существует ли тип void& ?

Нет.

6. Можно ли изменить значение по ссылке, если оно объявлено с ключевым словом const (объявлено как константное)?

Нет.

7. Как с помощью ссылки передать переменную или объект класса в функцию? Пример

С помощью ссылки можно передавать переменную или объект класса в функцию. Таким образом, в теле функции можно изменять значение переданной переменной.

Пример 1. Демонстрация передачи переменной типа int в функцию и изменения ее значения в теле функции. Приложение типа Win32 Console Application.

#include "stdafx.h"
#include <iostream>
using namespace std;

// функция Inc5() - увеличивает на 5
void Inc5(int & t) // передача t по ссылке
{
    t += 5;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int d;

    d = 5;
    Inc5(d); // d = 10
    Inc5(d); // d = 15

    return 0;
}

Пример 2. В примере демонстрируется передача объекта класса по ссылке. Пусть задан класс CRadius и функция Volume(). В функцию Volume() передается объект класса CRadius по ссылке.

Передача объекта класса по ссылке есть более эффективной в сравнении с передачей по значению. Так как при передаче объекта по значению все данные объекта копируются в стек. Если в объекте есть много данных, то это сильно увеличивает время выполнения при вызове функции.

Текст программы, созданной по шаблону Win32 Console Application:

#include "stdafx.h"
#include <iostream>
using namespace std;

class CRadius
{
    double radius;

    public:
    // конструкторы
    CRadius() { radius = 1; }
    CRadius(double nradius) { radius = nradius; }

    // методы доступа
    void Set(double nradius) { radius = nradius; }
    double Get() { return radius; }
};

// вычисляет объем шара
double Volume(CRadius & R) // передача по ссылке
{
    double vol, radius;
    radius = R.Get();
    vol = 4.0 / 3.0 * 3.1415 * radius * radius * radius;
    return vol;
}

int _tmain(int argc, _TCHAR* argv[])
{
    CRadius r(2.5); // экземпляр класса CRadius
    double vol;

    vol = Volume(r); // передача объекта класса по ссылке
    cout << vol << endl; // vol = 65.4479

    return 0;
}

8. Каким образом возвратить ссылку из функции? Примеры

Функция может возвращать ссылку на объект. В этом случае, вызов функции может размещаться в левой части оператора присваивания (см. пример 2).

Пример 1. Демонстрируется возвращение из функции ссылки на тип int.

#include "stdafx.h"
#include <iostream>
using namespace std;

// возвращение ссылки из функции
int & Inc5(int t) // функция увеличивает на 5 значение параметра t
{
    t = t + 5;
    return t;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a, b;
    b = 7;
    a = Inc5(b); // a = 12
    cout << a << endl;
    return 0;
}

Пример 2. Демонстрируется изменение значения внешнего (глобального) массива A с помощью функции Change(), которая возвращает ссылку на элемент массива A с заданным индексом.

Текст демонстрационной программы, созданной по шаблону Win32 Console Application имеет следующий вид

#include "stdafx.h"
#include <iostream>
using namespace std;

#define MAX 20
double A[MAX];

// функция, которая возвращает ссылку на элемент массива A с индексом index
double & Change(int index)
{
    // возвратить значение элемента массива A
    return A[index];
}

int _tmain(int argc, _TCHAR* argv[])
{
    int i;

    // заполнение массива
    for (i=0; i<MAX; i++)
        A[i] = i * 2; // A = { 0.0, 2.0, 4.0, ..., 38.0 }

    // изменить значение элемента массива с индексом 5 с помощью функции
    ChangeA(5) = 3.25; // A[5] = 3.25;
    ChangeA(MAX-1) = -7.77; // A[19] = -7.77;

    return 0;
}

9. Примеры инициализации структурной переменной с использованием ссылок

Показаны два способа инициализации структурной переменной.

Пример 1. Возврат ссылки на структурную переменную. Инициализация структурной переменной осуществляется в функции InitPixel().

#include "stdafx.h"
#include <iostream>
using namespace std;

// структура пиксель
struct Pixel
{
    int x, y, color;
};

// функция, которая возвращает ссылку на структуру
Pixel & InitPixel(int value)
{
    Pixel px;
    px.x = px.y = px.color = value;
    return px;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Pixel px;
    Pixel px2 = InitPixel(5); // px2.x = 5; px2.y = 5; px2.color = 5;

    px = InitPixel(11); // px.x = 11; px.x = 11; px.x = 11;
    cout << px.x << " - " << px.y << " - " << px.color << endl;
    cout << px2.x << " - " << px2.y << " - " << px2.color << endl;

    return 0;
}

Пример 2. Инициализация структурной переменной типа Pixel. Функция InitPixel() получает ссылку на структурную переменную в качестве параметра.

#include "stdafx.h"
#include <iostream>
using namespace std;

// структура пиксель
struct Pixel
{
    int x, y, color;
};

// функция, которая возвращает ссылку на структуру
void InitPixel(Pixel & px, int x, int y, int color)
{
    // изменение значения структурной переменной у функции
    px.x = x;
    px.y = y;
    px.color = color;
    return;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Pixel px;
    Pixel px2;

    InitPixel(px, 3, 8, 10); // px.x = 3; px.y = 8; px.color = 11;
    InitPixel(px2, 5, 5, 8); // px2.x = 5; px2.y = 5; px2.color = 8;

    cout << px.x << " - " << px.y << " - " << px.color << endl;
    cout << px2.x << " - " << px2.y << " - " << px2.color << endl;

    return 0;
}

10. Какие существуют ограничения на работу со ссылками в C++?

В языке C++ работа с оссылками имеет свои ограничения:

  • ссылки не могут связываться с другими ссылками (только с переменными);
  • не существует нулевой ссылки;
  • ссылки должны быть инициализированы при их объявлении.


Связанные темы