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

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

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

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

- X использует имя Y

- X использует Y

- X вызывает функцию-член Y

- X читает член Y

- X пишет в член Y

- X создает Y

- X размещает auto или static переменную из Y

- X создает Y с помощью new

- X использует размер Y

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

для этого требуется знание полного определения класса. С другой

стороны, мы выделили в отдельное отношение использование имени Y,

поскольку, указывая его в описании Y* или в описании

внешней функции, мы вовсе не нуждаемся в доступе к определению Y:

class Y; // Y - имя класса

Y* p;

extern Y f(const Y&);

Мы отделили создание Y с помощью new от случая описания

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

для создания Y с помощью new необязательно знать

размер Y. Это может быть существенно для ограничения всех зависимостей

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

Язык С++ не требует, чтобы создатель классов точно определял,

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

заключена в том, что самые важные классы зависят от столь большого

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

нужна сокращенная форма записи списка используемых классов, например,

с помощью команды #include. Другая причина в том, что классификация

этих зависимостей и, в частности, обЪединение некоторых зависимостей

не является обязанностью языка программирования. Наоборот, цели

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

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

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

специфики реализации языка.

12.2.7 Отношения внутри класса

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

если не считать обсуждения шагов процесса развития программного

обеспечения ($$11.3.3.2), то они были на втором плане, объекты же

практически вообще не упоминались. Понять это просто: в С++

класс, а не функция или объект, является основным понятием

организации системы.

Класс может скрывать в себе всякую специфику реализации,

наравне с "грязными" приемами программирования, а иногда он

вынужден это делать. В то же время объекты большинства классов

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

что их достаточно просто описать. Объект класса может быть

совокупностью других вложенных объектов (их часто называют членами),

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

на другие объекты. Поэтому отдельный объект можно рассматривать как

корень дерева объектов, а все входящие в него объекты как "иерархию

объектов", которая дополняет иерархию классов, рассмотренную в $$12.2.4.

Рассмотрим в качестве примера класс строк из $$7.6:

class String {

int sz;

char* p;

public:

String(const char* q);

~String();

//...

};

Объект типа String можно изобразить так:

12.2.7.1 Инварианты

Значение членов или объектов, доступных с помощью членов класса,

называется состоянием объекта (или просто значением объекта).

Главное при построении класса - это: привести объект в полностью

определенное состояние (инициализация), сохранять полностью определенное

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

и в конце работы уничтожить объект без всяких последствий. Свойство,

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

инвариантом.

Поэтому назначение инициализации - задать конкретные значения,

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

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

операции и должен сохраниться после операции. В конце работы

деструктор нарушает инвариант, уничтожая объект. Например,

конструктор String::String(const char*) гарантирует,

что p указывает на массив из, по крайней мере, sz элементов, причем

sz имеет осмысленное значение и v[sz-1]==0. Любая строковая операция

не должна нарушать это утверждение.

При проектировании класса требуется большое искусство, чтобы

сделать реализацию класса достаточно простой и допускающей

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

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

инвариант, который понятен и не накладывает жестких ограничений

на действия разработчика класса или на эффективность реализации.

Здесь "инвариант" понимается как программный фрагмент,

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

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

некоторых ситуациях оно может оказаться более подходящим. Здесь же

под инвариантом понимается практическая, а значит, обычно экономная,

но неполная проверка состояния объекта.

Понятие инварианта появилось в работах Флойда, Наура и Хора,

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

статьях по абстрактным типам данных и верификации программ за

последние 20 лет. Оно же является основным предметом отладки в C++.

Обычно, в течение работы функции-члена инвариант не сохраняется.

Поэтому функции, которые могут вызываться в те моменты, когда

инвариант не действует, не должны входить в общий интерфейс класса.

Такие функции должны быть частными или защищенными.

Как можно выразить инвариант в программе на С++? Простое решение -

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

функции в общие операции. Например:

class String {

int sz;

int* p;

public:

class Range {};

class Invariant {};

void check();

String(const char* q);

~String();

char& operator[](int i);

int size() { return sz; }

//...

};

void String::check()

{

if (p==0 || sz<0 || TOO_LARGE<=sz || p[sz-1])

throw Invariant;

}

char& String::operator[](int i)

{

check(); // проверка на входе

if (i<0 || i<sz) throw Range; // действует

check(); // проверка на выходе

return v[i];

}

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

Но для такого простого класса как String проверка инварианта будет

занимать большую часть времени счета. Поэтому программисты обычно

выполняют проверку инварианта только при отладке:

inline void String::check()

{

if (!NDEBUG)

if (p==0 || sz<0 || TOO_LARGE<=sz || p[sz])

throw Invariant;

}

Мы выбрали имя NDEBUG, поскольку это макроопределение, которое

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

С assert(). Традиционно NDEBUG устанавливается с целью указать,

что отладки нет. Указав, что check() является подстановкой, мы

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

NDEBUG не будет установлена в значение, обозначающее отладку.

С помощью шаблона типа Assert() можно задать менее регулярные

утверждения, например:

template<class T, class X> inline void Assert(T expr,X x)

{

if (!NDEBUG)

if (!expr) throw x;

}

вызовет особую ситуацию x, если expr ложно, и мы не отключили

проверку с помощью NDEBUG. Использовать Assert() можно так:

class Bad_f_arg { };

void f(String& s, int i)

{

Assert(0<=i && i<s.size(),Bad_f_arg());

//...

}

Шаблон типа Assert() подражает макрокоманде assert() языка С.

Если i не находится в требуемом диапазоне, возникает особая

ситуация Bad_f_arg.

С помощью отдельной константы или константы из класса проверить

подобные утверждения или инварианты - пустяковое дело. Если же

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

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

проверки, см. упр.8 в $$13.11.

Для классов с более сложными операциями расходы на проверки могут

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

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

мере несколько проверок даже в очень хорошо отлаженной программе.

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

их при отладке дает неоценимую помощь для получения правильной

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

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

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

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

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

и недосмотров.

Мы указали в $$11.3.3.5, что две самые общие формы преобразования

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

общей части двух классов в базовый класс. В обоих случаях хорошо

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

преобразования. Если, сравнивая инвариант с программами операций,

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

то значит класс созрел для разбиения. В этом случае подмножество операций

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

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

при некотором различии в их реализации.

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

Отметим, что в С++ класс, а не отдельный объект, является той

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

Например:

class list {

list* next;

public:

int on(list*);

};

int list::on(list* p)

{

list* q = this;

for(;;) {

if (p == q) return 1;

if (q == 0) return 0;

q = q->next;

}

}

Здесь обращение к частному указателю list::next допустимо, поскольку

list::on() имеет доступ ко всякому объекту класса list, на который

у него есть ссылка. Если это неудобно, ситуацию можно упростить,

отказавшись от возможности доступа через функцию-член к

представлениям других объектов, например:

int list::on(list* p)

{

if (p == this) return 1;

if (p == 0) return 0;

return next->on(p);

}

Но теперь итерация превращается в рекурсию, что может сильно

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

не сумеет обратно преобразовать рекурсию в итерацию.

12.2.8 Программируемые отношения

Конкретный язык программирования не может прямо поддерживать

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

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

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

на языковые конструкции. Например, метод проектирования может

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

операция, которая не определена для класса A, должна выполняться

в нем с помощью указателя p на соответствующий член класса B,

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

реализация этого понятия настолько в духе С++, что легко представить

программу реализации:

class A {

B* p;

//...

void f();

void ff();

};

class B {

//...

void f();

void g();

void h();

};

Тот факт, что В делегирует A с помощью указателя A::p,

выражается в следующей записи:

class A {

B* p; // делегирование с помощью p

//...

void f();

void ff();

void g() { p->g(); } // делегирование q()

void h() { p->h(); } // делегирование h()

};

Для программиста совершенно очевидно, что здесь происходит, однако здесь

явно нарушается принцип взаимнооднозначного соответствия. Такие

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

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

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

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