6 (972474)
Текст из файла
Семинар 6. Наследование. Включение объектов и закрытоенаследование. Множественное наследование. Шаблоны классов.1. Наследование и динамическое распределение памятиПусть в базовом классе осуществляется динамическое распределение памяти сприменением оператора new [] в конструкторе и delete [] в деструкторе. Соответственно,переопределены конструктор копирования и оператор присваивания.
Если …1.1 В производном классе не используется оператор new [].В этом случае никаких особых действий предпринимать ненужно. Заданный поумолчанию деструктор производного класса в конце своего кода вызовет деструкторбазового класса, который выполнит «уборку мусора». В конструкторе копированияэлементы копируются, зависящим от типа элементов способом. Поэтому при копированииунаследованного объекта базового класса будет вызван его уже перегруженныйнадлежащим образом конструктор копирования, в котором проблема динамическоговыделения памяти решена. Тоже относится и к оператору присваивания.1.2 В производном классе используется оператор new [].В этом случае для производного класса требуется определить явные деструктор,конструктор копирования и оператор присваивания.
Предположим, базовый Basefoo ипроизводный от него Derfoo классы имеют вид:class Basefoo{private:char* bp; //указатель для работы с динамической памятью…};class Derfoo : public Basefoo{private: //char* dp;…};Деструкторы этих классов должны убрать мусор каждый за «своим» объектом:Basefoo :: ~Basefoo(){delete [] bp;};Derfoo :: ~Derfoo(){delete [] dp;};Конструкторы копирования также обеспечивают копирование элементов в «зоне своейответственности»:Basefoo::Basefoo(const Basefoo & ob){int len = strlen(ob.bp)+1;bp = new char[len];strncpy(bp, ob.bp, len);};Derfoo::Derfoo(const Derfoo & ob):Basefoo(ob){int len = strlen(ob.dp)+1;dp = new char[len];strncpy(dp, ob.dp, len);};При этом конструктор копирования производного класса через список инициализаторовпосле знака : вызывает конструктор копирования базового класса, которому передаетсяссылка на объект производного класса.
Правилами наследования это допускается, т.к.ссылка базового класса может принимать значение объекта производного класса.Аналогично реализуется «распределение обязанностей» и при переопределенииоператоров присваивания. Оператор присваивания производного класса можетосуществить явный вызов оператора присваивания базового класса через операторнуюфункцию:Derfoo & Derfoo::operator=(const Derfoo & ob){if (this == &ob) return *this;Basefoo::operator=(ob); // присваивание элементов базового классаdelete [] dp; // уничтожение ранее выделенной памятиint len = strlen(ob.dp)+1;dp = new char[len];strncpy(dp, ob.dp, len);return *this;};2. Включение объектовКласс может в качестве элемента включать другой класс.
В примере объект класса С2включает в качестве своего элемента объект класса C1:class C1{private:int c1_i;public:C1(int i=0) {c1_i = i;}int read() {return c1_i;}};class C2{private:int c2_i;C1 ob;public:C2(int i=0): ob(i+2), c2_i(i) {} //конструктор//класса со списком инициализаторовint read() {return ob.read();}};Такое включение, формально не являясь наследованием, фактически реализует типнаследования has-a (включает). Класс С2 имеет доступ к элементам включенного класса С1через его общедоступные методы, а «внешний мир» - только через методы класса С2, т.к.этот класс объявил объект класса С1 в разделе private. Говорят, что класс С2 получаетреализацию объектов класса С1, но не наследует интерфейс.Для инициализации элементов включенного объекта ob в конструкторе класса С2предусмотрен список инициализаторов, в котором используется имя объекта ob.
При этомдолжен существовать соответствующий конструктор класса С1. Инициализация элементовосуществляется не в списочном порядке, а в порядке их объявления в классе. Если нетявного списка инициализаторов, то для включенных объектов вызывается конструктор поумолчанию.3. Закрытое наследованиеПри закрытом наследовании базовый класс объявляется с атрибутом private (это атрибутпо умолчанию, его можно опустить):class C2 : private C1{private:int c2_i;public:C2(int i): C1(i+2), c2_i(i) {} //конструктор классаint read() {return this->C1::read();}};В отличие от метода включения при наследовании к производному классу добавляетсянеименованный объект базового класса. Для его инициализации в конструкторе2производного класса используется вызов конструктора базового класса (в спискеинициализаторов).В отличие от открытого наследования публичные методы базового класса становятсясобственными методами производного класса.
Тем самым, так же как и в методевключения объекта, интерфейс не наследуется. Это еще один вариант наследования типа«содержится (has-a)». Чтобы предоставить доступ «миру» к данным базового объектаможно объявить в производном классе публичный метод, который, в свою очередь,вызовет нужный метод базового класса. В приведенном выше примере – это метод read().Обратите внимание, как происходит внутри этого метода обращение к одноименномуметоду базового класса: с классификатором диапазона доступа C1::.
В противном случаеполучился бы рекуррентный вызов самого себя, что привело бы к ошибке исполнения.Закрытое наследование для реализации модели has-a используется тогда, когда нужноиметь доступ к защищенным методам базового класса или иметь возможностьпереопределения виртуальных функций. В других случаях чаще применяется включениеобъектов. При этом их может быть несколько штук одного класса. По имени они легкоразличаются.4. Множественное наследованиеЭто такое наследование, когда производныйнепосредственный базовый класс.
Например,классимеетболеечемclass C3:public C1, public C2 {…};один(*)Множественное наследование порождает специфические проблемы. Например,наследование различных методов с одним и тем же именем из различных базовых классови транзитивное наследование нескольких экземпляров одного класса через нескольконепосредственных базовых классов.Пусть реализована следующая схема открытого множественного наследования:class C0class C1class C2class C3которое предполагает наряду с объявлением (*) наличие следующих объявлений:class C1:public C0 {…};class C2:public C0 {…};Виртуальный базовый класс.
Поскольку и объект класса С1, и объект класса С2 наследуюткопию объекта С0, то объект класса С3 по умолчанию унаследует две копии объекта С0. Вбольшинстве случаев это ненужно. Для исключения такого транзитивного дублированияобъектов введено понятие виртуального базового класса. В этом случае при объявлениипроизводных классов С1 и С2 используется ключевое слово virtual:class C1: virtual public C0 {…};class C2: virtual public C0 {…};3Пусть для примера каждый из базовых классов имеет частную целочисленнуюпеременную и общедоступный метод read() для ее чтения:class C0{int iC0;public:C0(int i=10): iC0(i) {};int read() const {return iC0;}};class C2: virtual public C0{int iC2;public:C2(int i=2): C0(2), iC2(i) {};int read() const {return iC2;}};class C1: virtual public C0{int iC1;public:C1(int i=1): C0(1), iC1(i) {};int read()const {return iC1;}};class C3: public C1, public C2{int iC3;public:C3(int i): C1(4), C2(5), iC3(i) {};};Простое обращение к методу read() для объекта производного класса C3 вызовет ошибкув силу неоднозначности этого вызова.
В этом случае следует указать диапазон доступа ::для явного выбора метода, например:C3 c3(9);cout << c3.C2::read();Аналогичным способом можно вызвать метод и исходного базового класса С0. Если бы водном из классов С1 или С2 метод read() отсутствовал, то вызов метода read() в классе C3был бы однозначным и без указания диапазона доступа. В этом случае методпроизводного класса имеет приоритет над одноименным методом базового класса.Отсутствие в списке инициализаторов конструктора класса C3 явного обращения кконструктору базового класса С0 приведет к тому, что унаследованный объект этогокласса получит значение по умолчанию, т.е.
10.5. Вложенные классыКласс, объявленный внутри другого класса, называется вложенным. Обычно вложениеиспользуется, чтобы избежать конфликта имен. В отличие от включения объекта другогокласса при вложении не создается элемент класса, а определяется тип, который локальноизвестен классу, содержащему вложение. Внешний мир может использовать вложенныйкласс с указанием диапазона доступа и только в том случае, если вложение выполнено вобщедоступном разделе. Предположим, есть класс D0 с вложенным классом D1:class D0{public:class D1 {};…}Создать объект ob класса D1 вне класса D0 можно следующим образом:D0::D1 ob;Доступ к элементам вложенного класса определяется обычными правилами и зависит, вкаком разделе (public, private или protected) они объявлены.
То, что один классопределен внутри другого, с точки зрения доступа к элементам класса не дает привилегийни тому, ни другому классу.46. Обобщенное программирование. Шаблоны классаШаблоны обеспечивают параметризованные типы, т.е. возможность передачи имени типав качестве аргумента, который содержит в себе указание для создания класса (илифункции).
Определение шаблона начинается с ключевого слова template:template <typename T>В угловых скобках с помощью ключевого слова typename (или class) перечисляютсяимена типов (их может быть несколько). С помощью шаблонов затем можно генерироватьразличные классы. Пусть, например, нам нужно два функционально «похожих» класса.Один для хранения и чтения целочисленной переменной, другой – переменной типаdouble. Вместо объявлений двух классов можно определить один шаблон cпараметризованным типом T:template <typename T>class C{T var;public:C (T arg) : var(arg+1) {};T read() const {return var;}};Сам по себе шаблон не приводит к созданию класса.
Класс будет создан (по шаблону)после объявления в программе объекта типа шаблона класса с желаемым типом в угловыхскбках. Например, так:C<int> obt(7);или(и) так:C<double> obt(7.5);В результате компилятор создаст класс(ы), в котором везде параметр типа T будет замененна желаемый тип, и создаст объект(ы) этого класса. При этом фактически именем классабудет являться C<int> и(или) C<double>. Можно создать конкретный класс без созданияего экземпляров, использовав следующее объявление:template class C<int>;Так же как и в случае шаблонов функций для определенного типа можно изменитьшаблон.
Характеристики
Тип файла PDF
PDF-формат наиболее широко используется для просмотра любого типа файлов на любом устройстве. В него можно сохранить документ, таблицы, презентацию, текст, чертежи, вычисления, графики и всё остальное, что можно показать на экране любого устройства. Именно его лучше всего использовать для печати.
Например, если Вам нужно распечатать чертёж из автокада, Вы сохраните чертёж на флешку, но будет ли автокад в пункте печати? А если будет, то нужная версия с нужными библиотеками? Именно для этого и нужен формат PDF - в нём точно будет показано верно вне зависимости от того, в какой программе создали PDF-файл и есть ли нужная программа для его просмотра.