Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Что такое ссылка и что с ней можно делать




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

  1. При объявлении ссылка обязательно инициализируется ссылкой на уже существующий объект данного типа.
  2. Ссылка пожизненно указывает на один и тот же адрес.
  3. При обращении к ссылке операция * производится автоматически.

Объявление ссылок очень похоже на объявление указателей, только вместо звёздочки «*» нужно писать амперсанд «&».В Си++ можно передавать параметры по ссылкам

void foo (int& x) { x = 17;}int main () { int z = 5; foo (z); // теперь z=17}

 

  • при вызове функции foo компилятор сам передаст адрес переменной z, нет необходимости специально его просить;
  • внутри функции foo мы обращаемся с x, как с обычной переменной, и только компилятор знает, что внутри это — указатель.

Итак, мы можем передавать по ссылкам параметры. Но ссылки могут существовать и сами по себе, вне всякой связи с передачей параметров.

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

Запись X& обозначает ссылку на X. Например:

 

int i = 1;

int &r = i; // r и i ссылаются на одно и то же целое

int x = r; // x = 1

r = 2; // i = 2;

Ссылка должна быть инициализирована, т.е. должно быть нечто, что она может обозначать. Следует помнить, что инициализация ссылки совершенно отличается от операции присваивания. Хотя можно указывать операции над ссылкой, ни одна из них на саму ссылку не действует, например,

int ii = 0;

int &rr = ii;

rr++; // ii увеличивается на 1

Инициализатор для типа T должен быть адресом. Однако, инициализатор для &T может быть и не адресом, и даже не типом T. В таких случаях делается следующее:

[1] во-первых, если необходимо, применяется преобразование типа;

[2] затем получившееся значение помещается во временную переменную;

[3] наконец, адрес этой переменной используется в качестве инициализатора ссылки.

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

Ссылка может использоваться для функции, которая изменяет значение своего параметра. Кроме перечисленного, с помощью ссылок можно определить функции, используемые как в правой, так и в левой частях присваивания. Наиболее интересное применение это обычно находит при определении нетривиальных пользовательских типов. Идея заключается в том, что со строкой связывается некоторое целое значение.

Использование спецификатора void

 

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

void f() // f не возвращает значение

void* pv; // указатель на объект неизвестного типа

 

Переменной типа void* можно присваивать указатель любого типа. На первый взгляд это может показаться не особенно полезным, поскольку void* нельзя разыменовать, но именно это ограничение и делает тип void* полезным. Главным образом, он применяется для передачи указателей функциям, которые не позволяют сделать предположение о типе объекта, и для возврата из функций нетипизированных объектов. Чтобы использовать такой объект, необходимо применить явное преобразование типа. Подобные функции обычно находятся на самом нижнем уровне системы, там, где осуществляется работа с основными аппаратными ресурсами. Например:

void* allocate(int size); // выделить

void deallocate(void*); // освободить

 

f() {

int* pi = (int*)allocate(10*sizeof(int));

char* pc = (char*)allocate(10);

//...

deallocate(pi);

deallocate(pc);

}

 

Преобразование стандартных типов данных.

 

Определяемые пользователем преобразования типа, например, такие, как преобразование числа с плавающей точкой в комплексное, которое необходимо для конструктора complex(double), оказались очень полезными в С++. Программист может задавать эти преобразования явно, а может полагаться на транслятор, который выполняет их неявно в том случае, когда они необходимы и однозначны:

 

complex a = complex (1);

complex b = 1; // неявно: 1 -> complex (1)

a = b + complex (2);

a = b + 2; // неявно: 2 -> complex (2)

 

Преобразования типов нужны в С++ потому, что арифметические операции со смешанными типами являются нормой для языков, используемых в числовых задачах. Кроме того, большая часть пользовательских типов, используемых для «вычислений» (например, матрицы, строки, машинные адреса) допускает естественное преобразование в другие типы (или из других типов).

Преобразования типов способствуют более естественной записи программы:

 

complex a = 2;

complex b = a + 2; // это означает: operator + (a, complex (2))

b = 2 + a; // это означает: operator + (complex (2), a)

 

В обоих случаях для выполнения операции "+" нужна только одна функция, а ее параметры единообразно трактуются системой типов языка. Более того, класс complex описывается так, что для естественного и беспрепятственного обобщения понятия числа нет необходимости что-то изменять для целых чисел.

 

Если перед выражением указать имя типа в круглых скобках, то значение выражения будет преобразовано к указанному типу.

 

 

Новые операции new, delete, операция расширения области видимости

 

Операция new

Идеология языка C++ предполагает, что каждый объект создаётся (объявляется) именно в том месте, где он нужен, и является работоспособным сразу после создания. Для этого каждый класс имеет определёный набор конструкторов - функций, которые должны автоматически запускаться при создании объекта (экземпляра данного класса) и инициализировать его члены. Конструкторы одного класса отличаются только количеством и типом передаваемых параметров, то есть явяются перегруженными функциями. В языке C++ им имеется адекватная замена malloc() - оператор new. Рассмотрим пример:

MyClass *mc = new MyClass(5);

В данном случае создаётся экземпляр класса MyClass, после чего с помощью его конструктора, принимающего в качестве параметра целое число (в данном случае, число 5), объект "инициализируется" этим числом. Адрес вновь созданного объекта присваивается указателю mc.

Важное отличие оператора new от функции malloc() заключается в том, что он возвращает значение типа "указатель-на-объект" (то есть MyClass *), в то время как функция malloc() - "указатель-на-что-угодно" (void *). Подобная типизация в C++ - не редкость, она строже, чем та, что используется в Си, и, следовательно, менее ошибкоопасна.

Операция new предназначена для создания объекта типа имя-типа. Этот тип должен быть типом объекта и функции нельзя размещать с ее помощью, хотя указатели на функции можно.

 

выражение-размещения:

::opt new параметры-new opt имя-типа-new инициализатор-new

::opt new параметры-new opt (имя-типа) инициализатор-new

параметры-new:

(список-выражений)

имя-типа-new:

список-спецификаций-типа описатель-new opt

описатель-new:

* список-спецификаций-cv opt описатель-new opt

имя-класса:: список-спецификаций-cv opt описатель-new opt

описатель-new opt [ выражение ]

инициализатор-new:

(список-инициализаторов opt)

 

Время жизни объекта, созданного с помощью new, не ограничивается областью видимости, в которой он был создан. Операция new возвращает указатель на созданный объект. Если объект является массивом, возвращается указатель на начальный элемент массива. Допускается, чтобы вызывалась функция operator new() с параметром нуль. В таком случае возвращается указатель на объект. При повторении таких вызовов будут возвращаться указатели на разные объекты.Для резервирования памяти операция new обращается к функции operator new(). При размещении объекта типа T ей в качестве первого параметра передается sizeof(T). Если с помощью операции new создается объект не типа класс (в том числе и массив объектов типа класс), то вызывается глобальная функция::operator new(). Если с помощью new создается объект класса T, вызывается функция T::operator new(), если она существует (используя обычные правила просмотра при поиске членов класса и его базовых классов), иначе вызывается глобальная функция::operator new(). Использование операции::new() гарантирует, что будет вызываться глобальная функция::operator new(), даже если существует T::operator new(). Если класс имеет конструктор, объект этого класса можно создать с помощью new только при условии, что заданы подходящие параметры, или, что класс имеет стандартный конструктор.Как для конструктора, так и для функции operator new() проводится проверка возможности доступа и однозначности.

 

Операция delete

Операция delete уничтожает объект, созданный с помощью new.

выражение-освобождения:

::opt delete выражение-приведения

::opt delete [] выражение-приведения

Результат имеет тип void. Операндом delete должен быть указатель, который возвращает new. Эффект применения операции delete к указателю, который не получен в результате операции new без задания параметры-new, считается неопределенным и обычно приводит к опасным последствиям. Однако гарантируется, что удаление по указателю с нулевым значением безопасно.

Результат попытки доступа к удаленному объекту неопределен, а удаление объекта может изменить его значение. Более того, если выражение, задающее объект, является изменяемым адресом, его значение после удаления неопределено.Нельзя удалять указатель на константу.Операция delete вызывает деструктор (если он есть) для объекта, на который настроен ее операнд.Для освобождения памяти, отведенной под указываемый объект, операция delete вызывает функцию operator delete. Для объектов, не имеющих тип класс (в том числе и для массивов классов), используется глобальная функция::operator delete(). Для объекта типа класс T вызывается функция T::operator delete(), если она есть (используя обычные правила просмотра при поиске членов класса и производных от него классов), в противном случае вызывается глобальная функция::operator delete(). Обращение::delete гарантирует, что будет вызываться глобальная функция::operator delete(), даже если существует T::operator delete(). Для удаления массивов используется обращение вида

 

delete [ ] выражение-приведения

 

Здесь выражение должно указывать на массив. Если есть деструкторы, они будут вызываться для удаления указанных объектов.

Результат удаления массива с помощью простого обращения delete неопределен, так же как и удаление одиночного объекта с помощью delete [].

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...