Язык программирования Си++ (1119468), страница 6
Текст из файла (страница 6)
*/ };• Все элементы, унаследованные производным классом отбазового, становятся закрытыми:class student4с: private student { /* ... */ }; // эквивалентно:class student4с: student { /* ... */ };116Одиночное наследование в Си++• Во вновь порождённом производном классе могут бытьопределены новые члены (как новые поля данных, так и новыеметоды): в конструкторе класса студентов 2 курса уже не нужнозадавать год обучения, но необходимо задать тему ируководителя практикой• При вызове конструктора для экземпляра класса студентов 2курса сначала проработает конструктор базового классаstudent (), а затем конструктор производного класса student2c,иерархически вложенного в базовый, который вправе ожидать,что имя студента, номер зачётной книжки, средний балл и годобучения будут уже определены, когда придётся заполнять полятемы и имени руководителя• При работе производного деструктора ~student2с () поле годаобучения ещё будет существовать117Одиночное наследование в Си++• Функция print () базового класса не видна объектампроизводного класса, но путём уточнения её имени именемкласса, которому принадлежит функция, её можно сделатьвидимой, если она не имеет атрибута privatevoid student :: print (){ cout << “ФИО= ” << name<< endl;cout << “Курс= ” << year<< endl;cout << “Средний балл = ” << avb<< endl;cout << “Номер зачётки = ” << student_id << endl;}void student2c :: print (){ student :: print (); // выдаёт в файл name, year, avb, student_idcout << “Тема курсовой = ” << pract<< endl;cout << “Преподаватель = ” << tutor<< endl;}118Одиночное наследование в Си++• Манипуляции с объектами классов в глобальной функции f ():void f (){ student s (“Катя”, 2, 4.18, 20100210);student2c ds (“Таня”, 4.08, 20100211, “Компилятор Си++”,“Виктор Петрович”);student * ps = & s; // указатель на базовый класс (курс = 2)student2c * pds = & ds; // указатель на производный классps -> print (); // student :: print () напечатает главноеpds -> print (); // student2c :: print () напечатает всёps = pds;// допустимо (преобразование по умолчанию)ps -> print (); // student :: print () выбирается//статически по типу указателя}119Генеалогические преобразованияpsnameyearсюда не...можетуказывать pds,так как здесь нет всехнеобходимых для типа*pds полейpdsnameyear...pract...ps = & s; pds = & ds; ps = pds; // допустимо!pds = ps;// ОШИБКА: недопустимо!pds = & s;// ОШИБКА: недопустимо!cюда можетуказывать ps,так как в этомобъекте имеютсявсе поля,относящиеся ктипу *psоднако, доступко всем полям поуказателю psневозможен,полный доступвозможен толькопо указателю pds120Генеалогические преобразованияpsnameyearсюда не...можетуказывать pds,так как здесь нет всехнеобходимых для типа*pds полейps = pds;ps = & ds;pdsnameyear...pract...// base * = derived * (безопасно)// base * = derived * (безопасно)С объектом производного класса можно обращаться какс объектом базового класса при обращении к нему припомощи базовых указателей и ссылокcюда можетуказывать ps,так как в этомобъекте имеютсявсе поля,относящиеся ктипу *psоднако, доступко всем полям поуказателю psневозможен,полный доступвозможен толькопо указателю pds121Генеалогические преобразования• Возможность доступа к объектам по указателямопределяется статически по типу используемогоуказателя• Правило статического выбора объекта по типууказателя работает даже в условиях длинныхцепочек производных классов:class X1 { public: void f (int);/* ...
*/ };class X2 : public X1 { /* ... */ }; /* ... */class X10 : public X9 { public: void f (double); /* ... */ };void g (X10 * p) { p -> f (2); } // X10::f или X1::f?122Виды полиморфизма• Полиморфными называются методы, позволяющие выразитьалгоритм один раз, но использовать его с множеством разныхтипов• Статический полиморфизм действует только на этапекомпиляции и характеризуется использованием одноимённыхфункций с разными профилями• Параметрический (ти́повый) полиморфизм временикомпиляции связан с введением шаблонов как средствапараметрического описания классов• Динамический полиморфизм реализуется с помощьювиртуальных функций и доступа к функциям по указателям,когда на этапе компиляции не удаётся определить точнуювызываемую функцию, так как значение указателя не известно123Динамический полиморфизм• Поддержку динамического полиморфизма выполняютвиртуальные методы, связанные с полиморфными объектами• Виртуальные методы вводятся в классах, когда предполагается,что впоследствии будут определены производные от нихклассы, в которых эти виртуальные методы будутпереопределены и сделаны конкретными, имеющими болееточные реализации• В каждом из производных классов переопределениевиртуальных методов может быть своё, не похожее насделанное в других классах• Целью введения виртуальных методов является заменастатического выбора метода (по типу указателя), надинамический выбор (по типу объекта, а не указателя)124Работа виртуальной функции• Полиморфным называется тип, в состав которого входятвиртуальные функцииclass student {/* ...
*/ public: void print () const; };class student2c: public student { /* ... */ public: void print () const; };student :: ~student () { delete [] name; }student2c :: ~student2c() { delete [] pract; delete [] tutor; }void student :: print (){ cout << “ФИО= ” << name<< endl;cout << “Курс= ” << year<< endl;cout << “Средний балл = ” << avb<< endl;cout << “Номер зачётки = ” << student_id << endl; }void student2c :: print (){ student :: print (); // выдаёт в файл name, year, avb, student_idcout << “Тема курсовой = ” << pract<< endl;cout << “Преподаватель = ” << tutor<< endl; }125Работа виртуальной функции• Манипуляции с объектами классов в глобальной функции f ():void f (){ student s (“Катя”, 2, 4.18, 20100210);student2c ds (“Таня”, 4.08, 20100211, “Компилятор Си++”,“Виктор Петрович”);student * ps = & s; // указатель на базовый классstudent2c * pds = & ds; // указатель на производный классps -> print (); // student :: print () напечатает главноеpds -> print (); // student2c :: print () напечатает всёps = pds;// допустимо (преобразование по умолчанию)ps -> print (); // student :: print () выбирается}//статически по типу указателя126Работа виртуальной функции• Полиморфным называется тип, в состав которого входятвиртуальные функцииclass student {/* ...
*/ public: virtual void print () const; };class student2c: public student { /* ... */ public: void print () const; };student :: ~student () { delete [] name; }student2c :: ~student2c() { delete [] pract; delete [] tutor; }void student :: print (){ cout << “ФИО= ” << name<< endl;cout << “Курс= ” << year<< endl;cout << “Средний балл = ” << avb<< endl;cout << “Номер зачётки = ” << student_id << endl; }void student2c :: print (){ student :: print (); // выдаёт в файл name, year, avb, student_idcout << “Тема курсовой = ” << pract<< endl;cout << “Преподаватель = ” << tutor<< endl; }127Работа виртуальной функции• Манипуляции с объектами классов в глобальной функции f ():void f (){ student s (“Катя”, 2, 4.18, 20100210);student2c ds (“Таня”, 4.08, 20100211, “Компилятор Си++”,“Виктор Петрович”);student * ps = & s; // указатель на базовый классstudent2c * pds = & ds; // указатель на производный классps -> print (); // student :: print () напечатает главноеpds -> print (); // student2c :: print () напечатает всёps = pds;// допустимо (преобразование по умолчанию)ps -> print (); // student2c :: print () выбирается}//динамически по типу объекта128Правила созданиявиртуальных функций1.
Имеется иерархия классов (без иерархии нет виртуальности!),хотя бы из двух классов – базового и производного2. В базовом классе функция объявлена с ключевым словомvirtual3. В производном классе есть функция с таким же именем, стаким же списком параметров (количество, типы и порядокпараметров в точности совпадают) и с таким же типомвозвращаемого значения4. Вызов функции осуществляется через указатель или ссылку наобъект базового класса без указания самого объекта иуточнения области видимости129Правила созданиявиртуальных функций• В производном классе слово virtual можетотсутствовать• Простое использование слова virtual в базовомклассе не запускает механизм виртуальности• Нарушение условий приводит к тому, что вместовиртуальной функции возникает обычное перекрытиеимён базового класса производным• В базовом классе, как таковом, никаких виртуальныхфункций существовать не может130Правила созданиявиртуальных функций• Типы возвращаемых значений могут иметь отличия: если вбазовом классе этот тип есть тип указателя (ссылки) на базовыйкласс, а в производном классе – тип указателя (ссылки) напроизводный класс, то виртуальность всё же достигается• Допускается исключение: если в виртуальной функциибазового класса типом возвращаемого значения являетсяуказатель на сам тип этого класса, то в функции производногокласса допускается иметь в качестве возвращаемого значениятакже указатель на тип производного класса:class student{ public: virtual student * cv () const; };class student2c:public student { public: virtual student2c * cv () const; };131Правила созданиявиртуальных функций• Если в производном классе нет объявления функции,одноимённой с функцией, обозначенной в базовомклассе как виртуальная, функция из базового классанаследуется, причём со словом virtual, то естьстановится потенциальной виртуальной функцией наслучай возникновения нового поколениянаследующих классов132Правила созданиявиртуальных функций• Вызов функции через объект (x.f ()) приводит краннему связыванию даже для виртуальных функций,виртуальности не возникает• При непосредственной работе с объектами (безуказателей и ссылок) их тип всегда известен:void fvirt (){ /* ...