Главная » Просмотр файлов » Бьерн Страуструп

Бьерн Страуструп (947334), страница 36

Файл №947334 Бьерн Страуструп (Стpаустpуп - Книга о C++) 36 страницаБьерн Страуструп (947334) страница 362013-09-15СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 36)

что является общим в этих классах. Способом такого указания может

служить виртуальный базовый класс.

Виртуальный базовый класс можно использовать для представления

"головного" класса, который может конкретизироваться разными

способами:

class window {

// головная информация

virtual void draw();

};

Для простоты рассмотрим только один вид общей информации из класса

window - функцию draw(). Можно определять разные более развитые

классы, представляющие окна (window). В каждом определяется своя

(более развитая) функция рисования (draw):

class window_w_border : public virtual window {

// класс "окно с рамкой"

// определения, связанные с рамкой

void draw();

};

class window_w_menu : public virtual window {

// класс "окно с меню"

// определения, связанные с меню

void draw();

};

Теперь хотелось бы определить окно с рамкой и меню:

class window_w_border_and_menu

: public virtual window,

public window_w_border,

public window_w_menu {

// класс "окно с рамкой и меню"

void draw();

};

Каждый производный класс добавляет новые свойства окна. Чтобы

воспользоваться комбинацией всех этих свойств, мы должны

гарантировать, что один и тот же объект класса window используется

для представления вхождений базового класса window в эти

производные классы. Именно это обеспечивает описание window во

всех производных классах как виртуального базового класса.

Можно следующим образом изобразить состав объекта класса

window_w_border_and_menu:

Чтобы увидеть разницу между обычным и виртуальным наследованием,

сравните этот рисунок с рисунком из $$6.5, показывающим состав объекта

класса satellite. В графе наследования каждый базовый класс с данным

именем, который был указан как виртуальный, будет представлен

единственным объектом этого класса. Напротив, каждый базовый

класс, который при описании наследования не был указан как

виртуальный, будет представлен своим собственным объектом.

Теперь надо написать все эти функции draw(). Это не слишком

трудно, но для неосторожного программиста здесь есть ловушка.

Сначала пойдем самым простым путем, который как раз к ней и ведет:

void window_w_border::draw()

{

window::draw();

// рисуем рамку

}

void window_w_menu::draw()

{

window::draw();

// рисуем меню

}

Пока все хорошо. Все это очевидно, и мы следуем образцу определения

таких функций при условии единственного наследования ($$6.2.1), который

работал прекрасно. Однако, в производном классе следующего уровня

появляется ловушка:

void window_w_border_and_menu::draw() // ловушка!

{

window_w_border::draw();

window_w_menu::draw();

// теперь операции, относящиеся только

// к окну с рамкой и меню

}

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

все операции, необходимые для базовых классов, а затем те, которые

относятся собственно к производным классам. Но в результате

функция window::draw() будет вызываться дважды! Для большинства

графических программ это не просто излишний вызов, а порча

картинки на экране. Обычно вторая выдача на экран затирает первую.

Чтобы избежать ловушки, надо действовать не так поспешно. Мы

отделим действия, выполняемые базовым классом, от действий,

выполняемых из базового класса. Для этого в каждом классе введем

функцию _draw(), которая выполняет нужные только для него

действия, а функция draw() будет выполнять те же действия плюс

действия, нужные для каждого базового класса. Для класса window

изменения сводятся к введению излишней функции:

class window {

// головная информация

void _draw();

void draw();

};

Для производных классов эффект тот же:

class window_w_border : public virtual window {

// класс "окно с рамкой"

// определения, связанные с рамкой

void _draw();

void draw();

};

void window_w_border::draw()

{

window::_draw();

_draw(); // рисует рамку

};

Только для производного класса следующего уровня проявляется

отличие функции, которое и позволяет обойти ловушку с повторным

вызовом window::draw(), поскольку теперь вызывается window::_draw()

и только один раз:

class window_w_border_and_menu

: public virtual window,

public window_w_border,

public window_w_menu {

void _draw();

void draw();

};

void window_w_border_and_menu::draw()

{

window::_draw();

window_w_border::_draw();

window_w_menu::_draw();

_draw(); // теперь операции, относящиеся только

// к окну с рамкой и меню

}

Не обязательно иметь обе функции window::draw() и window::_draw(),

но наличие их позволяет избежать различных простых описок.

В этом примере класс window служит хранилищем общей для

window_w_border и window_w_menu информации и определяет интерфейс

для общения этих двух классов. Если используется единственное

наследование, то общность информации в дереве классов достигается

тем, что эта информация передвигается к корню дерева до тех

пор, пока она не станет доступна всем заинтересованным в ней

узловым классам. В результате легко возникает неприятный эффект:

корень дерева или близкие к нему классы используются как пространство

глобальных имен для всех классов дерева, а иерархия классов вырождается

в множество несвязанных объектов.

Существенно, чтобы в каждом из классов-братьев переопределялись

функции, определенные в общем виртуальном базовом классе. Таким

образом каждый из братьев может получить свой вариант операций,

отличный от других. Пусть в классе window есть общая функция

ввода get_input():

class window {

// головная информация

virtual void draw();

virtual void get_input();

};

В одном из производных классов можно использовать эту функцию,

не задумываясь о том, где она определена:

class window_w_banner : public virtual window {

// класс "окно с заголовком"

void draw();

void update_banner_text();

};

void window_w_banner::update_banner_text()

{

// ...

get_input();

// изменить текст заголовка

}

В другом производном классе функцию get_input() можно определять,

не задумываясь о том, кто ее будет использовать:

class window_w_menu : public virtual window {

// класс "окно с меню"

// определения, связанные с меню

void draw();

void get_input(); // переопределяет window::get_input()

};

Все эти определения собираются вместе в производном классе следующего

уровня:

class window_w_banner_and_menu

: public virtual window,

public window_w_banner,

public window_w_menu

{

void draw();

};

Контроль неоднозначности позволяет убедиться, что в классах-братьях

определены разные функции:

class window_w_input : public virtual window {

// ...

void draw();

void get_input(); // переопределяет window::get_input

};

class window_w_input_and_menu

: public virtual window,

public window_w_input,

public window_w_menu

{ // ошибка: оба класса window_w_input и

// window_w_menu переопределяют функцию

// window::get_input

void draw();

};

Транслятор обнаруживает подобную ошибку, а устранить неоднозначность

можно обычным способом: ввести в классы window_w_input и

window_w_menu функцию, переопределяющую "функцию-нарушителя", и

каким-то образом устранить неоднозначность:

class window_w_input_and_menu

: public virtual window,

public window_w_input,

public window_w_menu

{

void draw();

void get_input();

};

В этом классе window_w_input_and_menu::get_input() будет

переопределять все функции get_input(). Подробно механизм разрешения

неоднозначности описан в $$R.10.1.1.

6.6 Контроль доступа

Член класса может быть частным (private), защищенным (protected)

или общим (public):

Частный член класса X могут использовать только функции-члены и

друзья класса X.

Защищенный член класса X могут использовать только функции-члены

и друзья класса X, а так же функции-члены и друзья всех

производных от X классов (см. $$5.4.1).

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

Эти правила соответствуют делению обращающихся к классу функций на три

вида: функции, реализующие класс (его друзья и члены), функции,

реализующие производный класс (друзья и члены производного класса) и

все остальные функции.

Контроль доступа применяется единообразно ко всем именам. На

контроль доступа не влияет, какую именно сущность обозначает имя.

Это означает, что частными могут быть функции-члены, константы и т.д.

наравне с частными членами, представляющими данные:

class X {

private:

enum { A, B };

void f(int);

int a;

};

void X::f(int i)

{

if (i<A) f(i+B);

a++;

}

void g(X& x)

{

int i = X::A; // ошибка: X::A частный член

x.f(2); // ошибка: X::f частный член

x.a++; // ошибка: X::a частный член

}

6.6.1 Защищенные члены

Дадим пример защищенных членов, вернувшись к классу window из

предыдущего раздела. Здесь функции _draw() предназначались только для

использования в производных классах, поскольку предоставляли неполный

набор возможностей, а поэтому не были достаточны удобны и

надежны для общего применения. Они были как бы строительным

материалом для более развитых функций. С другой стороны, функции draw()

предназначались для общего применения. Это различие можно выразить,

разбив интерфейсы классов window на две части - защищенный интерфейс

и общий интерфейс:

class window {

public:

virtual void draw();

// ...

protected:

void _draw();

// другие функции, служащие строительным материалом

private:

// представление класса

};

Такое разбиение можно проводить и в производных классах, таких, как

window_w_border или window_w_menu.

Префикс _ используется в именах защищенных функций, являющихся

частью реализации класса, по общему правилу: имена, начинающиеся с _ ,

не должны присутствовать в частях программы, открытых для общего

использования. Имен, начинающихся с двойного символа подчеркивания,

лучше вообще избегать (даже для членов).

Вот менее практичный, но более подробный пример:

class X {

// по умолчанию частная часть класса

int priv;

protected:

int prot;

public:

int publ;

void m();

};

Для члена X::m доступ к членам класса неограничен:

void X::m()

{

priv = 1; // нормально

prot = 2; // нормально

publ = 3; // нормально

}

Член производного класса имеет доступ только к общим и защищенным

членам:

class Y : public X {

void mderived();

};

Y::mderived()

{

priv = 1; // ошибка: priv частный член

prot = 2; // нормально: prot защищенный член, а

// mderived() член производного класса Y

publ = 3; // нормально: publ общий член

}

В глобальной функции доступны только общие члены:

void f(Y* p)

{

p->priv = 1; // ошибка: priv частный член

p->prot = 2; // ошибка: prot защищенный член, а f()

// не друг или член классов X и Y

p->publ = 3; // нормально: publ общий член

}

6.6.2 Доступ к базовым классам

Подобно члену базовый класс можно описать как частный, защищенный

или общий:

class X {

public:

int a;

// ...

};

class Y1 : public X { };

class Y2 : protected X { };

class Y3 : private X { };

Поскольку X - общий базовый класс для Y1, в любой функции, если есть

необходимость, можно (неявно) преобразовать Y1* в X*, и притом

в ней будут доступны общие члены класса X:

void f(Y1* py1, Y2* py2, Y3* py3)

{

X* px = py1; // нормально: X - общий базовый класс Y1

py1->a = 7; // нормально

px = py2; // ошибка: X - защищенный базовый класс Y2

py2->a = 7; // ошибка

px = py3; // ошибка: X - частный базовый класс Y3

py3->a = 7; // ошибка

}

Теперь пусть описаны

class Y2 : protected X { };

class Z2 : public Y2 { void f(); };

Характеристики

Тип файла
Документ
Размер
4,26 Mb
Тип материала
Учебное заведение
Неизвестно

Список файлов книги

Свежие статьи
Популярно сейчас
А знаете ли Вы, что из года в год задания практически не меняются? Математика, преподаваемая в учебных заведениях, никак не менялась минимум 30 лет. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6513
Авторов
на СтудИзбе
302
Средний доход
с одного платного файла
Обучение Подробнее