Б. Страуструп - Язык программирования С++ (1119446), страница 37
Текст из файла (страница 37)
Ими пользоваться не менее удобно, чемвстроенными. В идеале использование определенного пользователем типа не должно отличаться отиспользования встроенных типов. Различия возможны только в способе построения.Тип есть вполне конкретное представление некоторого понятия. Например, в С++ тип float с операциями+, -, * и т.д. является хотя и ограниченным, но конкретным представлением математического понятиявещественного числа. Новый тип создается для того, чтобы стать специальным и конкретнымпредставлением понятия, которое не находит прямого и естественного отражения среди встроенныхтипов.
Например, в программе из области телефонной связи можно ввести тип trunk_module (линиясвязи), в видеоигре - тип explosion (взрыв), а в программе, обрабатывающей текст, - типlist_of_paragraphs (список-параграфов). Обычно проще понимать и изменять программу, в которой типыхорошо представляют используемые в задаче понятия. Удачно подобранное множествопользовательских типов делает программу более ясной. Оно позволяет транслятору обнаруживатьнедопустимое использование объектов, которое в противном случае останется невыявленным доотладки программы.Главное в определении нового типа - это отделить несущественные детали реализации (например,расположение данных в объекте нового типа) от тех его характеристик, которые существенны дляправильного его использования (например, полный список функций, имеющих доступ к данным).
Такоеразделение обеспечивается тем, что вся работа со структурой данных и внутрение, служебныеоперации над нею доступны только через специальный интерфейс (через "одно горло").Глава состоит из четырех частей:$$5.2 Классы и члены. Здесь вводится основное понятие пользовательского типа, называемого классом.Доступ к объектам класса может ограничиваться множеством функций, описания которыхвходят в описание класса. Эти функции называются функциями-членами и друзьями. Длясоздания объектов класса используются специальные функции-члены, называемыеконструкторами.
Можно описать специальную функцию-член для удаления объектов класса приего уничтожении. Такая функция называется деструктором.$$5.3 Интерфейсы и реализации. Здесь приводятся два примера разработки, реализации ииспользования классов.$$5.4 Дополнительные свойства классов. Здесь приводится много дополнительных подробностей оклассах. Показано, как функции, не являющейся членом класса, предоставить доступ к егочастной части. Такую функцию называют другом класса. Вводятся понятия статических членовкласса и указателей на члены класса.
Здесь же показано, как определить дискриминирующееобъединение.$$5.5 Конструкторы и деструкторы. Объект может создаваться как автоматический, статическийили как объект в свободной памяти. Кроме того, объект может быть членом некоторогоагрегата (массива или другого класса), который тоже можно размещать одним из этих трехспособов. Подробно объясняется использование конструкторов и деструкторов, описывается122Бьерн Страуструп.Язык программирования С++применение определяемых пользователем функций размещения в свободной памяти и функцийосвобождения памяти.5.2 Классы и членыКласс - это пользовательский тип. Этот раздел знакомит с основными средствами определения класса,создания его объектов, работы с такими объектами и, наконец, удаления этих объектов послеиспользования.5.2.1 Функции-членыПосмотрим, как можно представить в языке понятие даты, используя для этого тип структуры и наборфункций, работающих с переменными этого типа:struct date { int month, day, year; };date today;void set_date(date*, int, int, int);void next_date(date*);void print_date(const date*);// ...Никакой явной связи между функциями и структурой date нет.
Ее можно установить, если описатьфункции как члены структуры:struct date {int month, day, year;void set(int, int, int);void get(int*, int* int*);void next();void print();};Описанные таким образом функции называются функциями-членами. Их можно вызывать только черезпеременные соответствующего типа, используя стандартную запись обращения к члену структуры:date today;date my_birthday;void f(){my_birthday.set(30,12,1950);today.set(18,1,1991);my_birthday.print();today.next();}Поскольку разные структуры могут иметь функции-члены с одинаковыми именами, при определениифункции-члена нужно указывать имя структуры:void date::next(){if (++day > 28 ) {// здесь сложный вариант}}В теле функции-члена имена членов можно использовать без указания имени объекта.
В таком случаеимя относится к члену того объекта, для которого была вызвана функция.5.2.2 КлассыМы определили несколько функций для работы со структурой date, но из ее описания не следует, чтоэто единственные функции, которые предоставляют доступ к объектам типа date. Можно установить123Бьерн Страуструп.Язык программирования С++такое ограничение, описав класс вместо структуры:class date {int month, day, year;public:void set(int, int, int);void get(int*, int*, int*);void next();void print()};Служебное слово public (общий) разбивает описание класса на две части. Имена, описанные в первойчастной (private) части класса, могут использоваться только в функциях-членах.
Вторая - общая часть представляет собой интерфейс с объектами класса. Поэтому структура – это такой класс, в котором поопределению все члены являются общими. Функции-члены класса определяются и используются точнотак же, как было показано в предыдущем разделе:void date::print()// печать даты{cout << month << '/' << day << '/' << year ;}в принятом в США видеОднако от функций не членов частные члены класса date уже ограждены:void backdate(){today.day--;}// ошибкаЕсть ряд преимуществ в том, что доступ к структуре данных ограничен явно указанным спискомфункций. Любая ошибка в дате (например, December, 36, 1985) могла быть внесена только функциейчленом, поэтому первая стадия отладки - локализация ошибки – происходит даже до первого пускапрограммы.
Это только частный случай общего правила: любое изменение в поведении типа date можети должно вызываться изменениями в его членах. Другое преимущество в том, что потенциальномупользователю класса для работы с ним достаточно знать только определения функций-членов.Защита частных данных основывается только на ограничении использования имен членов класса.Поэтому ее можно обойти с помощью манипуляций с адресами или явных преобразований типа, но этоуже можно считать мошенничеством.5.2.3 Ссылка на себяВ функции-члене можно непосредственно использовать имена членов того объекта, для которого онабыла вызвана:class X {int m;public:int readm() { return m; }};void f(X aa, X bb){int a = aa.readm();int b = bb.readm();// ...}При первом вызове readm() m обозначает aa.m, а при втором - bb.m.У функции-члена есть дополнительный скрытый параметр, являющийся указателем на объект, длякоторого вызывалась функция.
Можно явно использовать этот скрытый параметр под именем this.Считается, что в каждой функции-члене класса X указатель this описан неявно какX *const this;124Бьерн Страуструп.Язык программирования С++и инициализируется, чтобы указывать на объект, для которого функция-член вызывалась. Этотуказатель нельзя изменять, поскольку он постоянный (*const). Явно описать его тоже нельзя, т.к. this это служебное слово. Можно дать эквивалентное описание класса X:class X {int m;public:int readm() { return this->m; }};Для обращения к членам использовать this излишне.
В основном this используется в функциях-членах,непосредственно работающих с указателями. Типичный пример - функция, которая вставляет элемент всписок с двойной связью:class dlink {dlink* pre; // указатель на предыдущий элементdlink* suc; // указатель на следующий элементpublic:void append(dlink*);// ...};void dlink::append(dlink* p){p->suc = suc;// т.е. p->suc = this->sucp->pre = this;// явное использование "this"suc->pre = p;// т.е. this->suc->pre = psuc = p;// т.е. this->suc = p}dlink* list_head;void f(dlink* a, dlink* b){// ...list_head->append(a);list_head->append(b);}Списки с такой общей структурой служат фундаментом списочных классов, описываемых в главе 8.Чтобы присоединить звено к списку, нужно изменить объекты, на которые настроены указатели this, preи suc.
Все они имеют тип dlink, поэтому функция-член dlink::append() имеет к ним доступ. Защищаемойединицей в С++ является класс, а не отдельный объект класса.Можно описать функцию-член таким образом, что объект, для которого она вызывается, будетдоступен ей только по чтению. Тот факт, что функция не будет изменять объект, для которого онавызывается (т.е. this*), обозначается служебным словом const в конце списка параметров:class X {int m;public:readme() const { return m; }writeme(int i) { m = i; }};Функцию-член со спецификацией const можно вызывать для постоянных объектов, а функцию-член безтакой спецификации - нельзя:void f(X& mutable, const X& constant){mutable.readme();// нормальноmutable.writeme(7); // нормальноconstant.readme();// нормальноconstant.writeme(7); // ошибка}125Бьерн Страуструп.Язык программирования С++В этом примере разумный транслятор смог бы обнаружить, что функция X::writeme() пытается изменитьпостоянный объект.
Однако, это непростая задача для транслятора. Из-за раздельной трансляции он вобщем случае не может гарантировать "постоянство" объекта, если нет соответствующего описания соспецификацией const. Например, определения readme() и writeme() могли быть в другом файле:class X {int m;public:readme() const;writeme(int i);};В таком случае описание readme() со спецификацией const существенно.Тип указателя this в постоянной функции-члене класса X есть const X *const.