Бьерн Страуструп (Стpаустpуп - Книга о C++), страница 9

2013-09-15СтудИзба

Описание файла

Документ из архива "Стpаустpуп - Книга о C++", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "книги и методические указания", в предмете "информатика" в общих файлах.

Онлайн просмотр документа "Бьерн Страуструп"

Текст 9 страницы из документа "Бьерн Страуструп"

s.push ( kitty );

cat c2 = s.pop ();

// ...

}

Поскольку интерфейс стека ничего не сообщает о его представлении, от

пользователей стека полностью скрыты детали его реализации.

Можно предложить несколько различных реализаций стека. Например, стек

может быть массивом:

template < class T >

class astack : public stack < T >

{

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

// в данном случае - это массив

// ...

public:

astack ( int size );

~astack ();

void push ( T );

T pop ();

};

Можно реализовать стек как связанный список:

template < class T >

class lstack : public stack < T >

{

// ...

};

Теперь можно создавать и использовать стеки:

void g ()

{

lstack < cat > s1 ( 100 );

astack < cat > s2 ( 100 );

cat Ginger;

cat Snowball;

some_function ( s1, Ginger );

some_function ( s2, Snowball );

}

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

тот, кто их создает (т.е. функция g()), а пользователь стека (т.е. автор

функции some_function()) полностью огражден от деталей их реализации.

Платой за подобную гибкость является то, что все операции над стеками

должны быть виртуальными функциями.

1.5 Поддержка объектно-ориентированного программирования

Поддержку объектно-ориентированного программирования обеспечивают

классы вместе с механизмом наследования, а также механизм вызова

функций-членов в зависимости от истинного типа объекта (дело в том, что

возможны случаи, когда этот тип неизвестен на стадии трансляции). Особенно

важную роль играет механизм вызова функций-членов. Не менее важны

средства, поддерживающие абстракцию данных (о них мы говорили ранее). Все

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

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

поддерживающего объектно-ориентированное программирование. Успех обоих

методов зависит от способа построения типов, от того, насколько они

просты, гибки и эффективны. Метод объектно-ориентированного

программирования позволяет определять более общие и гибкие

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

использовать только абстракцию данных.

1.5.1 Механизм вызова

Основное средство поддержки объектно-ориентированного программирования

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

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

p. Как происходит вызов p->rotate(45)? Поскольку С++ базируется на

статическом контроле типов, задающее вызов выражение имеет смысл только

при условии, что функция rotate() уже была описана. Далее, из обозначения

p->rotate() мы видим, что p является указателем на объект некоторого

класса, а rotate должна быть членом этого класса. Как и при всяком

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

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

программе используются непротиворечивым образом. Тем самым гарантируется,

что программа свободна от многих видов ошибок.

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

тем, что приводились в $$1.2.5:

class shape

{

// ...

public:

// ...

virtual void rotate ( int );

// ...

};

а указатель p должен быть описан, например, так:

T * p;

где T - класс shape или производный от него класс. Тогда транслятор

видит, что класс объекта, на который настроен указатель p, действительно

имеет функцию rotate(), а функция имеет параметр типа int. Значит,

p->rotate(45) корректное выражение.

Поскольку shape::rotate() была описана как виртуальная функция, нужно

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

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

объекта некоторую служебную информацию, которая была помещена туда при его

создании. Как только установлено, какую функцию надо вызвать, допустим

circle::rotate, происходит ее вызов с уже упоминавшимся контролем типа.

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

функций, а транслятор преобразует имя rotate в индекс этой таблицы. С

учетом этой таблицы объект типа shape можно представить так:

center

vtbl:

color &X::draw

&Y::rotate

...

...

Функции из таблицы виртуальных функций vtbl позволяют правильно

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

неизвестны ни таблица vtbl, ни расположение данных в части объекта,

обозначенной ... . Здесь как X и Y обозначены имена классов, в которые

входят вызываемые функции. Для объекта circle оба имени X и Y есть circle.

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

вызов обычной функции.

1.5.2 Проверка типа

Необходимость контроля типа при обращениях к виртуальным функциям

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

Например, хорошо бы предоставить пользователю класс "стек чего-угодно".

Непосредственно в С++ это сделать нельзя. Однако, используя шаблоны типа и

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

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

динамическим контролем типов. К таким языкам относится, например, язык

Smalltalk, на котором можно описать "стек чего-угодно". Рассмотрим

определение стека с помощью шаблона типа:

template < class T > class stack

{

T * p;

int sz;

public:

stack ( int );

~stack ();

void push ( T );

T & pop ();

};

Не ослабляя статического контроля типов, можно использовать такой стек

для хранения указателей на объекты типа plane (самолет):

stack < plane * > cs ( 200 );

void f ()

{

cs.push ( new Saab900 ); // Ошибка при трансляции :

// требуется plane*, а передан car*

cs.push ( new Saab37B );

// прекрасно: Saab 37B - на самом

// деле самолет, т.е. типа plane

cs.pop () -> takeoff ();

cs.pop () -> takeoff ();

}

Если статического контроля типов нет, приведенная выше ошибка

обнаружится только при выполнении программы:

// пример динамическое контроля типа

// вместо статического; это не С++

Stack s; // стек может хранить указатели на объекты

// произвольного типа

void f ()

{

s.push ( new Saab900 );

s.push ( new Saab37B );

s.pop () -> takeoff (); // прекрасно: Saab 37B - самолет

cs.pop () -> takeoff (); // динамическая ошибка:

// машина не может взлететь

}

Для способа определения, допустима ли операция над объектом, обычно

требуется больше дополнительных расходов, чем для механизма вызова

виртуальных функций в С++.

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

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

динамический контроль типов. Класс в С++ задает строго определенный

интерфейс для множества объектов этого и любого производного класса, тогда

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

и пользователь вправе применять незаданные в классе операции. Иными

словами, класс в С++ содержит точное описание операций, и пользователю

гарантируется, что только эти операции транслятор сочтет допустимыми.

1.5.3 Множественное наследование

Если класс A является базовым классом для B, то B наследует атрибуты

A. т.е. B содержит A плюс еще что-то. С учетом этого становится очевидно,

что хорошо, когда класс B может наследовать из двух базовых классов A1 и

A2. Это называется множественным наследованием.

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

два библиотечных класса displayed и task. Первый представляет задачи,

информация о которых может выдаваться на экран с помощью некоторого

монитора, а второй - задачи, выполняемые под управлением некоторого

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

такие:

class my_displayed_task: public displayed, public task

{

// текст пользователя

};

class my_task: public task {

// эта задача не изображается

// на экране, т.к. не содержит класс displayed

// текст пользователя

};

class my_displayed: public displayed

{

// а это не задача

// т.к. не содержит класс task

// текст пользователя

};

Если наследоваться может только один класс, то пользователю доступны

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

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

происходит и то, и другое. Приведенный пример проходит в С++ безо всяких

дополнительных расходов времени и памяти по сравнению с программами, в

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

этого тоже не страдает.

Все неоднозначности выявляются на стадии трансляции:

class task

{

public:

void trace ();

// ...

};

class displayed

{

public:

void trace ();

// ...

};

class my_displayed_task:public displayed, public task

{

// в этом классе trace () не определяется

};

void g ( my_displayed_task * p )

{

p -> trace (); // ошибка: неоднозначность

}

В этом примере видны отличия С++ от объектно-ориентированных диалектов

языка Лисп, в которых есть множественное наследование. В этих диалектах

неоднозначность разрешается так: или считается существенным порядок

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

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

совпадение объектов доля базовых классов сочетается с более сложным

способом для производных классов. В С++ неоднозначность, как правило,

разрешается введением еще одной функции:

class my_displayed_task:public displayed, public task

{

// ...

public:

void trace ()

{

// текст пользователя

displayed::trace (); // вызов trace () из displayed

task::trace (); // вызов trace () из task

}

// ...

};

void g ( my_displayed_task * p )

{

p -> trace (); // теперь нормально

}

1.5.4 Инкапсуляция

Пусть члену класса (неважно функции-члену или члену, представляющему

данные) требуется защита от "несанкционированного доступа". Как разумно

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

ответ для языков, поддерживающих объектно-ориентированное

программирование, таков: доступ имеют все операции, которые определены для

этого объекта, иными словами, все функции-члены. Например:

class window

{

// ...

protected:

Rectangle inside;

// ...

};

class dumb_terminal : public window

{

// ...

public:

void prompt ();

// ...

};

Здесь в базовом классе window член inside типа Rectangle описывается

как защищенный (protected), но функции-члены производных классов,

например, dumb_terminal::prompt(), могут обратиться к нему и выяснить, с

какого вида окном они работают. Для всех других функций член

window::inside недоступен.

В таком подходе сочетается высокая степень защищенности

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

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

их иерархию (действительно, "для себя" всегда можно в производных классах

предусмотреть доступ к защищенным членам).

Неочевидное следствие из этого: нельзя составить полный и

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

поскольку всегда можно добавить еще одну, определив ее как функцию-член в

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

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

данных, то очевидное для него решение - это требование указывать в

описании класса список всех функций, которым нужен доступ к члену. В С++

для этой цели используется описание частных (private) членов. Оно

использовалось и в приводившихся описаниях классов complex и shape.

Важность инкапсуляции, т.е. заключения членов в защитную оболочку,

резко возрастает с ростом размеров программы и увеличивающимся разбросом

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

по инкапсуляции.

1.6 Пределы совершенства

Язык С++ проектировался как "лучший С", поддерживающий абстракцию

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