Когда функция-элемент ссылается на другой элемент какого-то объекта данного класса, откуда у С++ берется уверенность, что имеется ввиду соответствующий объект? Ответ заключается в том, что каждый объект сопровождается указателем на самого себя — называемым указателем this — это неявный аргумент во всех ссылках на элементы внутри этого объекта. Указатель this можно использовать также и явно. Каждый объект может определить свой собственный адрес с помощью ключевого слова this.
Указатель this неявно используется для ссылки как на данные-элементы, так и на функции-элементы объекта. Тип указателя this зависит от типа объекта и от того, объявлена ли функция-элемент, в которой используется this, как const. В неконстантной функции-элементе класса Employee указатель this имеет тип Employee * const (константный указатель на объект Employee). В константной функции-элементе класса Employee указатель this имеет тип const Employee * const (константный указатель на объект Employee, который тоже константный).
А теперь мы покажем простой пример явного использование указателя this; позже мы представим несколько реальных сложных примеров использования this. Каждая функция-элемент имеет доступ к указателю this на объект, для которого вызван этот элемент.
Совет по повышению эффективности 7.2
С целью экономии памяти для каждой функции-элемента существует только одна копия на класс и эта функция-элемент вызывается каждым объектом данного класса. С другой стороны, каждый объект имеет свою собственную копию данных-элементов класса.
Программа на рис. 7.7 демонстрирует явное использование указателя this, чтобы дать возможность функции-элементу класса Test печатать закрытую переменную х объекта Test.
С иллюстративными целями функция-элемент print на рис. 7.7 сначала печатает х непосредственно. Затем программа использует две различных записи для доступа к х посредством указателя this — операцию стрелки (->), примененную к указателю this, и операцию точка (.) для разыменования указателя this.
// FIG7_7.CPP
// Использование указателя this для ссылки на объекты-элементы. #include <iostream.h>
// конструктор по умолчанию |
class Test { public:
Test (int = 0); void print() const; private: int x;
} ;
// конструктор |
Test::Test(int a) { x = a; }
void Test::print() const {
cout « " « " « " |
x = this->x = (*this).x = |
« x « endl « this->x « endl «(*this).x « endl;
}
main() {
Test a(12) a.print(); return 0;
x = 12 this->x = 12 (♦this).x = 12
Рис. 7.7. Использование указателя this
Отметим круглые скобки, в которые заключено *this, когда используется операция доступа к элементу точка (.). Круглые скобки необходимы, так как операция точка имеет более высокий приоритет по сравнению с операцией *. Без круглых скобок выражение
*this.x
оценивалось бы так же, как если бы были круглые скобки следующего вида *(this.х)
Компилятор С++ воспринял бы это выражение как синтаксическую ошибку, так как операция доступа к элементу не может быть использована с указателем.
Попытка использовать операцию доступа к элементу (.) с указателем на объект (операцию доступа к элементу точка можно использовать только с объектом или со ссылкой на объект).
Одним интересным применением указателя this является предотвращение присваивания объекта самому себе. Как мы увидим в главе 8 «Перегрузка операций», самоприсваивание может стать причиной серьезных ошибок в случаях, когда объекты содержат указатели на динамически распределяемую память.
Другим применением указателя this является возможность сцепленных вызовов функций элементов. Программа на рис. 7.8 иллюстрирует возвращение ссылки на объект Time, которое дает возможность сцепления вызовов функций-элементов класса Time. Каждая из функций-элементов setTime, setHour, setMinute и setSecond возвращает *this с типом возврата Time &.
Почему возвращение *this работает как ссылка? Операция точка (.) имеет ассоциативность слева направо, так что выражение t.setHour(18).setMinute(30).setSecond(22) ;
сначала вычисляет t.setHour(18), а затем возвращает ссылку на объект t как значение вызова этой функции.
// TIME6.H
// Объявление класса Time.
// Функции-элементы определены в TIME6.CPP
lifndef TIME6_Н #define TIME6 H
class Time { public: Time(int = 0, int |
0, int = 0); // конструктор по умолчанию
// функции записи "set" Time &setTime(int, int,
Time SsetHour(int); Time SsetMinute(int); Time SsetSecond(int);
int); // установка часа, минут
II и секунд
// установка часа
II установка минут
II установка секунд
// функции чтения get (обычно объявляются const) int getHour() const; // возвращает значение часа
int getMinute() const; // возвращает значение минут
int getSecond() const; // возвращает значение секунд
// функции печати (обычно объявляются const) void printMilitary() const; II печать военного
// времени
void printStandard() const; // печать стандартного
// времени
private: int hour; int minute; int second; |
II 0-23 II 0-59 // 0-59 |
} ; #endif |
Рис 7.8. Сцепление вызовов функций-элементов (часть 1 из 5)
Оставшееся выражение затем интерпретируется как
t.setMinute(30).setSecond(22) ;
Вызов t.setMinute(30) выполняется и возвращает эквивалент t. Оставшееся выражение интерпретируется как
t.setSecond(22);
Отметим, что вызовы
t.setTime(20, 20, 20) .printStandard()
также используют особенности сцепления. Эти вызовы должны появляться именно в указанной последовательности, потому что printStandard, как описано в классе, не возвращает ссылку на t. Расположение вызова printStandard в предыдущем операторе перед вызовом setTime приводит к синтаксической ошибке.
// TIME6.CPP
// Определения функций-элементов класса Time. #include "time6.h" Iinclude
// Функция конструктор для задания начальных значений // закрытым данным.
// Вызов функций-элементов setTime для установки переменных. //По умолчанию значения равны 0 (смотри описание класса). Time::Time(int hr, int min, int sec) { setTime(hr, min, sec); }
// установка часа, минут и секунд.
Time STime::setTime(int h, int m, int s) {
hour = (h >= 0 SS h< 24) ? h: 0; minute = (m >= 0 SS m < 60) ? m : 0; second = (s >= 0 SS s < 60) ? s : 0;
return *this; // возможность сцепления
}
// Установка значения часа
Time STime::setHour(int h) {
hour = (h >= 0 SS h < 24) ? h : 0;
return *this; // возможность сцепления
}
// Установка значения минут
Time STime::setMinute(int m) {
minute = (m >= 0 ss m < 60) ? m : 0;
return *this; // возможность сцепления
}
Рис. 7.8. Сцепление вызовов функций-элементов (часть 2 из 5)
// TIME6.CPP: Продолжение // Установка значения секунд
Time STime::setSecond(int s) {
second = (s >= 0 && s < 60) ? s : 0;
return *this; // возможность сцепления
}
// Получение значения часа
int Time::getHour() const { return hour; }
// Получение значения минут
int Time::getMinute() const { return minute; } // Получение значения секунд
int Time::getSecond() const { return second; }
// Отображение времени в военном формате: HH:MM:SS
void Time::printMilitary() const {
cout « (hour < 10 ? "0":"") « hour « ":"
« (minute < 10 ? "0":"") « minute « ":" « (second < 10 ? "0":"") « second;
}
// Отображение времени в стандартном формате: HH:MM:SS AM (или РМ)
void Time::printStandard() const {
cout « ((hour == 0 || hour ==12) ? 12 : hour % 12) « ":" « (minute < 10 ? "0" : "") « minute « ":" « (second < 10 ? "0" : "") « second « (hour < 12 ? "AM": "PM");
}
Рис. 7.8. Сцепление вызовов функций-элементов (часть 3 из 5) // FIG7_8.CPP
// Сцепление вызовов функций-элементов указателем this iinclude ciostream.h> #include "time6.h"
main() {
Time t;
t.setHour(18).setMinute(30).setSecond(22); cout « "Военное время: "; t.printMilitary();
cout << endl « "Стандартное время: "; t.printStandard();
cout << endl <
Комментариев нет:
Отправить комментарий