- Основы ООП: абстракция
- Основы ООП: инкапусляция
- Основы ООП: наследование
- Основы ООП: полиморфизм
- Основы ООП: композиция (дизайн)
- Определение класса
- Права доступа
- Конструкторы: по умолчанию, параметрический, копирования
- Деструкторы
- Ссылка на себя: this
- Наследование
- Динамическое создание объектов: new и delete
- Указатели
- Виртуальные методы (virtual и override)
- Статические члены
- Вложенные типы, пространства имен и оператор контекста '::'
- Разнесение объявления и определения класса
- Абстрактные классы и интерфейсы
- Перегрузка операторов
- Приведение объектных типов: dynamic_cast и указатель void *
- Указатели, ссылки и const
- Управление наследованием: final и delete
Ссылка на себя: this
В контексте каждого класса, в коде его методов, доступна специальная ссылка на текущий объект: this. По сути это неявно определенная переменная. К ней применимы все приемы работы с переменными-объектами. В частности, её можно разыменовать, чтобы обратиться к полю объекта или вызвать метод. Например, следующие операторы в каком-либо методе класса Shape идентичны (метода draw взят условно, только для демонстрации):
class Shape
|
Необходимость использовать длинную форму может возникнуть, если в том же контексте существуют другие переменные/параметры с тем же именем. Обычно такая практика не приветствуется, но если в ней возникла необходимость, то ключевое слово this позволяет обратиться к перекрытым членам объекта.
Компилятор выводит предупреждение, если имя какой-либо локальной переменной или параметра метода перекрывает имя переменной-члена класса.
В следующем гипотетическом примере мы реализовали метод draw, который принимает необязательный строковый параметр backgroundColor с названием цвета. Поскольку имя параметра совпадает с членом класса Shape, компилятор выдает первое предупреждение "определение 'backgroundColor' скрывает поле".
Следствием перекрытия является то, что последующее ошибочное присвоение значения clrBlue работает с параметром, а не членом класса, и поскольку типы значения и параметра не совпадают, выдается второе предупреждение "неявное преобразование числа в строку" (числом является константа clrBlue). Зато строка this.backgroundColor = clrBlue записывает значение в поле объекта.
void draw(string backgroundColor = NULL) // предупреждение 1:
|
Последующее определение локальной логической переменной backgroundColor (во вложенном блоке фигурных скобок) перекрывает прежние определения этого имени еще раз (из-за чего получаем третье предупреждение). Однако благодаря разыменованию this оператор this.backgroundColor = clrRed также обращается к полю объекта.
Без указания this компилятор всегда выбирает самое близкое (по контексту) определение имени.
Существует необходимость в this и иного рода: передать текущий объект как параметр в другую функцию. В частности, применяется подход, при котором объекты одного класса ответственны за создание/удаление объектов другого класса, причем подчиненный объект должен знать своего "начальника". Тогда зависимые объекты создаются в классе "начальника" с помощью конструктора, в который передается this объекта-"начальника". Эта техника, как правило, применяет динамическое распределение объектов и указатели, и потому соответствующий пример будет показан в разделе с описанием указателей.
Другой способ частого применения this заключается в возврате указателя на текущий объект из функции-члена. Это позволяет выстраивать вызовы функций-членов в цепочку. Пока мы не изучали указатели подробно, будет достаточно знать, что указатель на объект какого-либо класса описывается путем добавления к имени класса символа '*', а работать с объектом через указатель можно также, как и напрямую.
Например, мы можем предоставить пользователю несколько методов для установки свойств фигуры по отдельности: смена цвета, перемещение по горизонтали или вертикали. Каждый из них будет возвращать указатель на текущий объект.
Shape *setColor(const color c)
|
Тогда есть возможность удобно нанизывать вызовы этих методов в цепочку.
Shape s;
|
Когда свойств в классе много, данный подход позволяет компактно и выборочно настраивать объект.
В разделе Определение класса мы пробовали вывести в журнал переменную-объект, но обнаружили, что можем использовать её имя лишь с амперсандом (в вызове Print), чтобы получить указатель, а фактически уникальный номер (дескриптор). В контексте объекта тот же дескриптор доступен через &this.
Для целей отладки можно идентифицировать объекты по дескриптору. Мы собираемся изучить наследование классов, и когда их станет больше одного, идентификация окажется кстати. Поэтому во всех конструкторах и деструкторах добавим (и будем в будущем добавлять в производных классах) такой вызов Print:
~Shape()
|
Теперь все этапы создания и удаления будут отмечаться в журнале именем класса и номером объекта.
Похожие конструкторы и деструкторы реализуем в структуре Pair, однако в структурах, к сожалению, не поддерживаются указатели, то есть запись &this невозможна. Поэтому их мы можем идентифицировать только по содержимому (в данном случае, по координатам):
struct Pair
|