Перейти на главную   
  helloworld.ru - документация и книги по программированию  
helloworld.ru - документация и книги по программированию
    главная     хостинг    
Поиск по сайту:  
Смотрите также
Языки программирования
C#
MS Visual C++
Borland C++
C++ Builder
Visual Basic
Quick Basic
Turbo Pascal
Delphi
JavaScript
Java
PHP
Perl
Assembler
AutoLisp
Fortran
Python
1C

Интернет-технологии
HTML
VRML
HTTP
CGI
FTP
Proxy
DNS
протоколы TCP/IP
Apache

Web-дизайн
HTML
Дизайн
VRML
PhotoShop
Cookie
CGI
SSI
CSS
ASP
PHP
Perl

Программирование игр
DirectDraw
DirectSound
Direct3D
OpenGL
3D-графика
Графика под DOS

Алгоритмы
Численные методы
Обработка данных

Сис. программирование
Драйверы

Базы данных
MySQL
SQL

Другое

Хостинг


Друзья
demaker.ru
Реклама

Лучший хостинг. Аренда серверов




helloworld.ru

Указатель this

Продолжаем определение класса ComplexType. Теперь объявим и определим функцию-член PrintVal, которая будет выводить значение чисел-объектов.

Прототип функции разместим в классе:

void PrintVal();

При определении функции используется квалифицированное имя:

void ComplexType::PrintVal()
{
cout << "(" << real << ", " << imag << "i)" << endl;
cout << (int)CTcharVal << ", " << x << "…" << endl;
}

Значения данных-членов объекта выводятся при выполнении выражения вызова функции PrintVal:

CDw1.PrintVal();

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

Среди операторов функции-члена PrintVal() нет ни одного оператора, который позволял бы определить, какому объекту принадлежат данные-члены. И, тем не менее, вызов этой функции для каждого из определённых и различным образом проинициализированных объектов, в том числе и для безымянного объекта, который создаётся в результате непосредственного вызова конструктора:

ComplexType(0.0,0.0, 1).PrintVal(); ,

а также вызов функции для объекта, адресуемого указателем:

pCD->PrintVal();

сопровождается сообщением о значениях собственных данных-членов. Заметим, что "собственные" данные-члены объектов, как и те функции-члены класса, с которыми мы уже успели познакомиться, считаются нестатическими данными и функциями-членами класса. Существуют также и статические члены класса, к изучению свойств которых мы обратимся в недалёком будущем.

Автоматическое определение принадлежности данных-членов конкретному объекту характерно для любой нестатической функции-члена класса. Объекты являются "хозяевами" нестатических данных и потому каждая нестатическая функция-член класса должна уметь распознавать "хозяйские" данные.

Вряд ли алгоритм распознавания хозяина данных очень сложен. Здесь проблема заключается совсем в другом: этот алгоритм должен быть реализован практически для каждой нестатической функции-члена класса. Он используется везде, где производится обращение к данным-членам объектов, а это означает, что на программиста может быть возложена дополнительная обязанность по кодированию. Несколько обязательных строк для каждой функции-члена? Да никогда…

К счастью, C++ освобождает программистов от утомительной и однообразной работы кодирования стандартного алгоритма распознавания. В C++ вообще многое делается без их участия. Функции-члены определяются как обычные функции. Транслятор переопределяет эти функции, обеспечивая при этом стандартными средствами связь между объектами и их данными. Эта связь реализуется благодаря специальному преобразованию исходного кода программы. Мы опишем это преобразование, условно разделив его на два этапа.

На первом этапе каждая нестатическая функция-член преобразуется в функцию с уникальным именем и дополнительным параметром - константным указателем на объект класса. Затем преобразуются обращения к нестатическим данным-членам в операторах функции-члена. Они переопределяются с учётом нового параметра. В C++ при подобном преобразовании для обозначения дополнительного параметра-указателя (константного указателя) и постфиксного выражения с операциями обращения для обращения к нестатическим данным-членам используется одно и то же имя this. Вот как могла бы выглядеть функция-член PrintVal после её переопределения:

void ComplexType::ComplexType_PrintVal(ComplexType const *this)
{
cout << "(" << this->real << "," << this->imag << "i)" << endl;
cout << int(this->CTcharVal) << "," << x << "…" << endl;
}

На втором этапе преобразуются вызовы функций-членов. К списку значений параметров выражения вызова добавляется выражение, значением которого является адрес данного объекта. Это вполне корректное преобразование. Дело в том, что нестатические функции-члены всегда вызываются для конкретного объекта. И потому не составляет особого труда определить адрес объекта. Например, вызов функции-члена PrintVal() для объекта CDw1, который имеет вид

CDw1.PrintVal();

после преобразования принимает вид:

ComplexType_PrintVal(&CDw1);

А вызов функции-члена безымянного объекта, адресуемого указателем pCD

pCD->PrintVal();

преобразуется к виду

ComplexType_PrintVal(&(*pCD));

что эквивалентно следующему оператору:

ComplexType_PrintVal(pCD);

Первый (и в нашем случае единственный) параметр в вызове новой функции является адресом конкретного объекта.

В результате такого преобразования функция-член приобретает новое имя и дополнительный параметр типа указатель на объект со стандартным именем this и типом, а каждый вызов функции-члена приобретает форму вызова обычной функции.

Причина изменения имени для функций-членов класса очевидна. В разных классах могут быть объявлены одноименные функции-члены. В этих условиях обращение к функции-члену класса непосредственно по имени может вызвать конфликт имён: в одной области действия имени одним и тем же именем будут обозначаться различные объекты - одноименные функции-члены разных классов. Стандартное преобразование имён позволяет решить эту проблему.

Указатель this можно использовать в теле функции-члена без его дополнительного объявления. В частности, операторы функции ComplexType::PrintVal() могут быть переписаны с использованием указателя this:

void ComplexType::PrintVal()
{
cout << "(" << this->real << "," << this->imag << "i)" << endl;
cout << int(this->CTcharVal) << "," << x << "…" << endl;
}

Явное употребление this указателя не вызывает у транслятора никаких возражений, что свидетельствует об эквивалентности старого и нового вариантов функции. В этом случае указатель this считается не именем (имя вводится объявлением), а первичным выражением. Напомним, что имя, как и первичное выражение this являются частными случаями выражения.

В ряде случаев при написании программы оправдано явное использование указателя this. При этом выражение

this

представляет адрес объекта, а выражение

*this

представляет сам объект:

this->ВЫРАЖЕНИЕ
(*this).ВЫРАЖЕНИЕ

(здесь нетерминальный символ ВЫРАЖЕНИЕ обозначает член класса). Эти выражения обеспечивают доступ к членам уникального объекта, представленного указателем this с целью изменения значения данного, входящего в этот объект или вызова функции-члена.

Следует помнить о том, что this указатель является константным указателем. Это означает, что непосредственное изменение его значение (перенастройка указателя, например, this++) недопустимо. Указатель this с самого начала настраивается на определённый объект.

При описании this указателя мы не случайно подчёркивали, что этот указатель используется только для нестатических функций-членов. Использование этого указателя в статических функциях-членах класса (о них речь впереди) не имеет смысла. Дело в том, что эти функции в принципе не имеют доступа к нестатическим данным-членам класса.

В объявлении нестатической функции-члена this указателю можно задавать дополнительные свойства. В частности, возможно объявление константного this указателя на константу. Синтаксис языка C++ позволяет сделать это. Среди БНФ, посвящённых синтаксису описателей, есть и такая форма:

Описатель ::=
         Описатель (СписокОбъявленийПараметров) [СписокCVОписателей]
          ::= *****

CVОписатель ::= const
            ::= *****

Так что небольшая модификация функции-члена PrintVal, связанная с добавлением cvОписателя const:

void PrintVal() const;

в прототипе и

void ComplexType::PrintVal() const
{
:::::
}

в определении функции обеспечивает относительную защиту данных от возможной модификации.

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

this->CTcharVal = 125;
real = imag*25;
imag++;

cvОписатель const в заголовке функции не допускает непосредственной модификации значений принадлежащих объекту данных.

Заметим также, что this указатель включается также в виде дополнительного параметра в список параметров конструктора. И в этом нет ничего удивительного, поскольку его значением является всего лишь область памяти, занимаемая объектом.


[ Назад | Оглавление | Далее ]









helloworld.ru © 2001-2021
Все права защищены