straustrup2 (852740), страница 47

Файл №852740 straustrup2 (Бьерн Страуструп. Язык программирования С++) 47 страницаstraustrup2 (852740) страница 472021-10-05СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

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

Они были как бы строительным материалом для более развитых функций. Сдругой стороны, функции draw() предназначались для общего применения. Это различие можновыразить, разбив интерфейсы классов window на две части - защищенный интерфейс и общийинтерфейс:171Бьерн Страуструп.Язык программирования С++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;prot = 2;publ = 3;////////ошибка: priv частный членнормально: prot защищенный член, аmderived() член производного класса Yнормально: publ общий член}В глобальной функции доступны только общие члены:void f(Y* p){p->priv = 1;p->prot = 2;p->publ = 3;////////}172ошибка: priv частный членошибка: prot защищенный член, а f()не друг или член классов X и Yнормально: 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 - общий базовый класс Y1py1->a = 7;// нормальноpx = py2;// ошибка: X - защищенный базовый класс Y2py2->a = 7;// ошибкаpx = py3;// ошибка: X - частный базовый класс Y3py3->a = 7;// ошибка}Теперь пусть описаныclass Y2 : protected X { };class Z2 : public Y2 { void f(); };Поскольку X - защищенный базовый класс Y2, только друзья и члены Y2, а также друзья и члены любыхпроизводных от Y2 классов (в частности Z2) могут при необходимости преобразовывать (неявно) Y2* вX*.

Кроме того они могут обращаться к общим и защищенным членам класса X:void Z2::f(Y1* py1, Y2* py2, Y3* py3){X* px = py1;// нормально: X - общий базовый класс Y1py1->a = 7;// нормальноpx = py2;// нормально: X - защищенный базовый класс Y2,// а Z2 - производный класс Y2py2->a = 7;// нормальноpx = py3;// ошибка: X - частный базовый класс Y3py3->a = 7;// ошибка}Наконец, рассмотрим:class Y3 : private X { void f(); };Поскольку X - частный базовый класс Y3, только друзья и члены Y3 могут при необходимостипреобразовывать (неявно) Y3* в X*. Кроме того они могут обращаться к общим и защищенным членамкласса X:void Y3::f(Y1* py1, Y2* py2, Y3* py3){X* px = py1;// нормально: X - общий базовый класс Y1py1->a = 7;// нормальноpx = py2;// ошибка: X - защищенный базовый класс Y2py2->a = 7;// ошибкаpx = py3;// нормально: X - частный базовый класс Y3,// а Y3::f член Y3py3->a = 7;// нормально173Бьерн Страуструп.Язык программирования С++}6.7 Свободная памятьЕсли определить функции operator new() и operator delete(), управление памятью для класса можновзять в свои руки.

Это также можно, (а часто и более полезно), сделать для класса, служащего базовымдля многих производных классов. Допустим, нам потребовались свои функции размещения иосвобождения памяти для класса employee ($$6.2.5) и всех его производных классов:class employee {// ...public:void* operator new(size_t);void operator delete(void*, size_t);};void* employee::operator new(size_t s){// отвести память в `s' байтов// и возвратить указатель на нее}void{//////}employee::operator delete(void* p, size_t s)`p' должно указывать на память в `s' байтов,отведенную функцией employee::operator new();освободить эту память для повторного использованияНазначение до сей поры загадочного параметра типа size_t становится очевидным.

Это - размеросвобождаемого объекта. При удалении простого служащего этот параметр получает значениеsizeof(employee), а при удалении управляющего - sizeof(manager). Поэтому собственные функцииклассы для размещения могут не хранить размер каждого размещаемого объекта. Конечно, они могутхранить эти размеры (подобно функциям размещения общего назначения) и игнорировать параметрsize_t в вызове operator delete(), но тогда вряд ли они будут лучше, чем функции размещения иосвобождения общего назначения.Как транслятор определяет нужный размер, который надо передать функции operator delete()? Пока тип,указанный в operator delete(), соответствует истинному типу объекта, все просто; но рассмотрим такойпример:class manager : public employee {int level;// ...};void f(){employee* p = new manager; // проблемаdelete p;}В этом случае транслятор не сможет правильно определить размер.

Как и в случае удаления массива,нужна помощь программиста. Он должен определить виртуальный деструктор в базовом классеemployee:class employee {// ...public:// ...void* operator new(size_t);174Бьерн Страуструп.Язык программирования С++void operator delete(void*, size_t);virtual ~employee();};Даже пустой деструктор решит нашу проблему:employee::~employee() { }Теперь освобождение памяти будет происходить в деструкторе (а в нем размер известен), а любойпроизводный от employee класс также будет вынужден определять свой деструктор (тем самым будетустановлен нужный размер), если только пользователь сам не определит его. Теперь следующийпример пройдет правильно:void f(){employee* p = new manager; // теперь без проблемdelete p;}Размещение происходит с помощью (созданного транслятором) вызоваemployee::operator new(sizeof(manager))а освобождение с помощью вызоваemployee::operator delete(p,sizeof(manager))Иными словами, если нужно иметь корректные функции размещения и освобождения для производныхклассов, надо либо определить виртуальный деструктор в базовом классе, либо не использовать вфункции освобождения параметр size_t.

Конечно, можно было при проектировании языкапредусмотреть средства, освобождающие пользователя от этой проблемы. Но тогда пользователь"освободился" бы и от определенных преимуществ более оптимальной, хотя и менее надежнойсистемы.В общем случае, всегда есть смысл определять виртуальный деструктор для всех классов, которыедействительно используются как базовые, т.е.

с объектами производных классов работают и, возможно,удаляют их, через указатель на базовый класс:class X {// ...public:// ...virtual void f();// в X есть виртуальная функция, поэтому// определяем виртуальный деструкторvirtual ~X();};6.7.1 Виртуальные конструкторыУзнав о виртуальных деструкторах, естественно спросить: "Могут ли конструкторы то же бытьвиртуальными?" Если ответить коротко - нет. Можно дать более длинный ответ: "Нет, но можно легкополучить требуемый эффект".Конструктор не может быть виртуальным, поскольку для правильного построения объекта он должензнать его истинный тип.

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

Допустим, что узлы, которые содержат повторяющиеся в выражении175Бьерн Страуструп.Язык программирования С++операции, нужно копировать только один раз. Тогда нам потребуется виртуальная функцияразмножения для узла выражения.Как правило "виртуальные конструкторы" являются стандартными конструкторами без параметров иликонструкторами копирования, параметром которых служит тип результата:class expr {// ...public:expr();// стандартный конструкторvirtual expr* new_expr() { return new expr(); }};Виртуальная функция new_expr() просто возвращает стандартно инициализированный объект типаexpr, размещенный в свободной памяти.

В производном классе можно переопределить функциюnew_expr() так, чтобы она возвращала объект этого класса:class conditional : public expr {// ...public:conditional(); // стандартный конструкторexpr* new_expr() { return new conditional(); }};Это означает, что, имея объект класса expr, пользователь может создать объект в "точности такого жетипа":void user(expr* p1, expr* p2){expr* p3 = p1->new_expr();expr* p4 = p2->new_expr();// ...}Переменным p3 и p4 присваиваются указатели неизвестного, но подходящего типа.Тем же способом можно определить виртуальный конструктор копирования, называемый операциейразмножения, но надо подойти более тщательно к специфике операции копирования:class expr {// ...expr* left;expr* right;public:// ...// копировать `s' в `this'inline void copy(expr* s);// создать копию объекта, на который смотрит thisvirtual expr* clone(int deep = 0);};Параметр deep показывает различие между копированием собственно объекта (поверхностноекопирование) и копированием всего поддерева, корнем которого служит объект (глубокое копирование).Стандартное значение 0 означает поверхностное копирование.Функцию clone() можно использовать, например, так:void fct(expr* root){expr* c1 = root->clone(1);expr* c2 = root->clone();// ...}// глубокое копирование// поверхностное копированиеЯвляясь виртуальной, функция clone() способна размножать объекты любого производного от expr176Бьерн Страуструп.класса.Язык программирования С++Настоящее копирование можно определить так:void expr::copy(expression* s, int deep){if (deep == 0) { // копируем только члены*this = *s;}else { // пройдемся по указателям:left = s->clone(1);right = s->clone(1);// ...}}Функция expr::clone() будет вызываться только для объектов типа expr (но не для производных от exprклассов), поэтому можно просто разместить в ней и возвратить из нее объект типа expr, являющийсясобственной копией:expr* expr::clone(int deep){expr* r = new expr(); // строим стандартное выражениеr->copy(this,deep);// копируем `*this' в `r'return r;}Такую функцию clone() можно использовать для производных от expr классов, если в них не появляютсячлены-данные (а это как раз типичный случай):class arithmetic : public expr {// ...// новых членов-данных нет =>// можно использовать уже определенную функцию clone};С другой стороны, если добавлены члены-данные, то нужно определять собственную функцию clone():class conditional : public expression {expr* cond;public:inline void copy(cond* s, int deep = 0);expr* clone(int deep = 0);// ...};Функции copy() и clone() определяются подобно своим двойникам из expression:expr* conditional::clone(int deep){conditional* r = new conditional();r->copy(this,deep);return r;}void conditional::copy(expr* s, int deep){if (deep == 0) {*this = *s;}else {expr::copy(s,1); // копируем часть exprcond = s->cond->clone(1);}}177Бьерн Страуструп.Язык программирования С++Определение последней функции показывает отличие настоящего копирования в expr::copy() отполного размножения в expr::clone() (т.е.

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

Тип файла
PDF-файл
Размер
2,87 Mb
Тип материала
Высшее учебное заведение

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

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