суббота, 5 декабря 2009 г.

7.5. Использование указателя this

Когда функция-элемент ссылается на другой элемент какого-то объекта данного класса, откуда у С++ берется уверенность, что имеется ввиду соот­ветствующий объект? Ответ заключается в том, что каждый объект сопро­вождается указателем на самого себя — называемым указателем this — это неявный аргумент во всех ссылках на элементы внутри этого объекта. Ука­затель this можно использовать также и явно. Каждый объект может опре­делить свой собственный адрес с помощью ключевого слова this.
Указатель this неявно используется для ссылки как на данные-элементы, так и на функции-элементы объекта. Тип указателя this зависит от типа объекта и от того, объявлена ли функция-элемент, в которой используется this, как const. В неконстантной функции-элементе класса Employee указа­тель this имеет тип Employee * const (константный указатель на объект Em­ployee). В константной функции-элементе класса 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() constII печать военного
// времени
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 <<< "Новое стандартное время: "; t.setTime(20, 20, 20).printStandard(); cout << endl; return 0;

Комментариев нет:

Отправить комментарий