Глава 10. Объектно-ориентированное программирование
10.3.2
Подробнее об указателях и ссылках на объекты
Передача объектов по указателю имеет ряд преимуществ. В стек копирует-
ся только ячейка памяти, содержащая адрес объекта, и автоматически исчезает
проблема корректного создания копии. Конечно в результате функция взаимо-
действует с оригиналом объекта, что требует осторожного изменения его данных
(а с копией можно делать всё что угодно). Ещё одно дополнительное преиму-
щество передачи по адресу в сравнении с передачей по значению — экономия
ресурсов на копирование. Если объект занимает достаточно большой объём па-
мяти, его передача в функцию и возвращение из неё, даже будучи выполнены
корректно, приведут к неоправданным расходам ресурсов.
Недостаток передачи по указателю — худшая читаемость программы, когда
приходится часто взаимодействовать с адресами объектов. Многочисленные опе-
рации адресации и разадресации (взятия адреса объекта и взятия содержимого
по адресу) могут ухудшать визуальное восприятие текста программы, особенно
в сочетании со скобками.
По этой причине в C++ был введён специальный тип данных — ссылка или
скрытый указатель
. На понятийном уровне ссылку можно воспринимать как
другое имя (псевдоним) переменной. Фактически же это указатель на перемен-
ную, который выглядит так, как будто к переменной обращаются по значению:
программист объявляет такую ссылку, присваивает ей какую-либо переменную
и далее пользуется ссылкой как ещё одной переменной. Компилятор же сам ав-
томатически подставляет ко всем обращениям к ссылке операции адресации и
разадресации.
Удобнее всего использовать ссылки для передачи параметров и возвращаемых
значений.
Напомним: ссылка объявляется так же как указатель, только с использова-
нием знака «&» вместо «звёздочки». Сравним, как выглядит код при передаче
аргумента по указателю и по ссылке, на примере функции zero(), устанавлива-
ющей в ноль координаты переданного ей объекта класса point:
//использование указателей
void z e r o ( p o i n t ∗p )
{
p−>s e t ( 0 , 0 ) ;
//мы использовали "−>"
}
main ( )
{
p o i n t a ( 3 , 4 ) ;
z e r o (&a ) ;
}
//использование ссылки
void z e r o ( p o i n t &p )
{
p . s e t ( 0 , 0 ) ;
//мы использовали " . "
}
main ( )
{
p o i n t a ( 3 , 4 ) ;
z e r o ( a ) ;
}
В приведённом примере при применении параметра-ссылки компилятор пе-
редаёт адрес переменной, но везде кроме объявления функции код выглядит так,
как будто переменная передана по значению. Аналогично ссылки могут исполь-
зоваться в качестве возвращаемого значения функции. Однако нельзя забывать,
Программирование на языке С++ в среде Qt Creator
10.3. Создание и удаление объектов
289
что функция, в которую передан параметр по ссылке, будет манипулировать не
копией
, а самим оригинальным объектом.
Часто ссылки применяют в сочетании с указателем this. Рассмотрим в каче-
стве примера переопределение оператора присваивания для класса point:
p o i n t& p o i n t : : operator= ( p o i n t& p )
{
x = p . x ;
y = p . y ;
return ∗ t h i s ;
}
В объявлении функции мы указали ссылочный тип в качестве как аргумен-
та, так и возвращаемого значения. Оператор присваивания должен возвращать
результат операции, чтобы стало возможным каскадное присваивание наподобие
a=b=c=0
. В качестве возвращаемого значения мы указываем разадресованный
указатель this, однако возвращён в качестве результата будет тот объект, кото-
рый вызывал операцию «=», а не его копия.
Приведём модифицированный вариант класса matrix, имеющий как кон-
структор копирования, так и оператор присваивания, и выдающий на экран пра-
вильный результат.
#include
using namespace s t d ;
c l a s s m a t r i x
{
double ∗m; //элементы матрицы
s i z e _ t width , h e i g h t ; //число строк и столбцов в матрице
public :
m a t r i x ( s i z e _ t w, s i z e _ t h ) ;
m a t r i x ( const matrix& m1) ; //конструктор копирования
m a t r i x& operator=( matrix & m1) ; //оператор присваивания
double g e t _ v a l ( s i z e _ t i , s i z e _ t j ) ;
void s e t _ v a l ( s i z e _ t i , s i z e _ t j , double v a l ) ;
~ m a t r i x ( ) ;
} ;
m a t r i x : : m a t r i x ( s i z e _ t w, s i z e _ t h )
{
m = new double [ w∗h ] ;
width = w ;
h e i g h t = h ;
}
m a t r i x : : m a t r i x ( const matrix& m1)
{
//устанавливаем размер матрицы и выделяем под неё память:
width = m1 . width ;
h e i g h t = m1 . h e i g h t ;
i n t s i z e=width ∗ h e i g h t ;
m = new double [ s i z e ] ;
//копируем элементы матрицы:
f o r ( i n t i =0; i < s i z e ; i ++)
m[ i ]=m1 .m[ i ] ;
}
m a t r i x& m a t r i x : : operator=( matrix& m1)
{
i n t s i z e=m1 . width ∗m1 . h e i g h t ;
i f ( s i z e > width ∗ h e i g h t )
//защищаемся от переполнения буфера
s i z e=width ∗ h e i g h t ;
m = new double [ s i z e ] ;
//копируем элементы матрицы:
© 2015 Алексеев Е. Р., Злобин Г. Г., Костюк Д. А., Чеснокова О. В., Чмыхало А. С.
290
Do'stlaringiz bilan baham: |