Ссылка на себя: this

В контексте каждого класса, в коде его методов, доступна специальная ссылка на текущий объект: this. По сути это неявно определенная переменная. К ней применимы все приемы работы с переменными-объектами. В частности, её можно разыменовать, чтобы обратиться к полю объекта или вызвать метод. Например, следующие операторы в каком-либо методе класса Shape идентичны (метода draw взят условно, только для демонстрации):

class Shape
{
   ...
   void draw()
   {
      backgroundColor = clrBlue;
      this.backgroundColor = clrBlue;
   }
};

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

Компилятор выводит предупреждение, если имя какой-либо локальной переменной или параметра метода перекрывает имя переменной-члена класса.

В следующем гипотетическом примере мы реализовали метод draw, который принимает необязательный строковый параметр backgroundColor с названием цвета. Поскольку имя параметра совпадает с членом класса Shape, компилятор выдает первое предупреждение "определение 'backgroundColor' скрывает поле".

Следствием перекрытия является то, что последующее ошибочное присвоение значения clrBlue работает с параметром, а не членом класса, и поскольку типы значения и параметра не совпадают, выдается второе предупреждение "неявное преобразование числа в строку" (числом является константа clrBlue). Зато строка this.backgroundColor = clrBlue записывает значение в поле объекта.

   void draw(string backgroundColor = NULL// предупреждение 1:
                // declaration of 'backgroundColor' hides member
   {
      ...
      backgroundColor = clrBlue// предупреждение 2:
        // implicit conversion from 'number' to 'string'
      this.backgroundColor = clrBlue// ok
      
      {
         bool backgroundColor = false// предупреждение 3:
             // declaration of 'backgroundColor' hides local variable
         ...
         this.backgroundColor = clrRed// ok
      }
      ...
   }

Последующее определение локальной логической переменной backgroundColor (во вложенном блоке фигурных скобок) перекрывает прежние определения этого имени еще раз (из-за чего получаем третье предупреждение). Однако благодаря разыменованию this оператор this.backgroundColor = clrRed также обращается к полю объекта.

Без указания this компилятор всегда выбирает самое близкое (по контексту) определение имени.

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

Другой способ частого применения this заключается в возврате указателя на текущий объект из функции-члена. Это позволяет выстраивать вызовы функций-членов в цепочку. Пока мы не изучали указатели подробно, будет достаточно знать, что указатель на объект какого-либо класса описывается путем добавления к имени класса символа '*', а работать с объектом через указатель можно также, как и напрямую.

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

   Shape *setColor(const color c)
   {
      backgroundColor = c;
      return &this;
   }
   
   Shape *moveX(const int x)
   {
      coordinates.x += x;
      return &this;
   }
 
   Shape *moveY(const int y)
   {
      coordinates.y += y;
      return &this;
   }

Тогда есть возможность удобно нанизывать вызовы этих методов в цепочку.

   Shape s;
   s.setColor(clrWhite).moveX(80).moveY(-50);

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

В разделе Определение класса мы пробовали вывести в журнал переменную-объект, но обнаружили, что можем использовать её имя лишь с амперсандом (в вызове Print), чтобы получить указатель, а фактически уникальный номер (дескриптор). В контексте объекта тот же дескриптор доступен через &this.

Для целей отладки можно идентифицировать объекты по дескриптору. Мы собираемся изучить наследование классов, и когда их станет больше одного, идентификация окажется кстати. Поэтому во всех конструкторах и деструкторах добавим (и будем в будущем добавлять в производных классах) такой вызов Print:

   ~Shape()
   {
      Print(__FUNCSIG__" ", &this);
   }

Теперь все этапы создания и удаления будут отмечаться в журнале именем класса и номером объекта.

Похожие конструкторы и деструкторы реализуем в структуре Pair, однако в структурах, к сожалению, не поддерживаются указатели, то есть запись &this невозможна. Поэтому их мы можем идентифицировать только по содержимому (в данном случае, по координатам):

struct Pair
{
   int xy;
   Pair(int aint b): x(a), y(b)
   {
      Print(__FUNCSIG__" "x" "y);
   }
   ...
};