При использовании множественного наследования, в протоколе производного класса необходимо вызывать конструктор базовых классов для инициализации полей данных, наследуемых от двух или более базовых классов, и инициализировать различные элементы объектов. Четко определенный порядок инициализации приведен ниже:
- Инициализация осуществляется в порядке, определяемом порядком объявления
- Члены инициализируются в порядке объявления
- Void-конструкторы базовых классов, которые явно не указаны в инициализирующем списки, вызываются после конструкторов явно инициализируемых базовых классов, в том порядке, в котором они следуют в объявлении класса. Эти void-конструкторы вызываются перед любыми конструкторами полей данных.
class Parent1 {
int p1;
public:
Parent1(void) { cout << “ Construct Parent1 free parameter”; p1=0; }
Parent1(int ap):p1(ap) { cout << “Construct Parent1 1 parameter”; }
~Parent1(void) { cout << “ destruct Parent1”; }
};
class Parent2 {
int p2;
public:
Parent2(void) { cout << “ Construct Parent2 free parameter”; p2=0; }
Parent2(int ap):p2(ap) { cout << “Construct Parent2 1 parameter”; }
~Parent2(void) { cout << “ destruct Parent2”; }
};
class Parent3 {
int p3;
public:
Parent3(void) { cout << “ Construct Parent3 free parameter”; p3=0; }
Parent3(int ap):p3(ap) { cout << “Construct Parent3 1 parameter”; }
~Parent3(void) { cout << “ destruct Parent3”; }
};
class Member {
int m;
public:
Member(void) { cout << “ Construct Member free parameter”; m=0; }
Member(int ap):m(ap) { cout << “Construct Member 1 parameter”; }
~Member(void) { cout << “ destruct Member”; }
};
class Child: public Parent3,public Parent2,public Parent3 {
Member mem1,mem2 ;
public:
Child(void) { cout << “ construct child free parameter”; }
Child(Member achildMember): childMember(achildMember) {
cout << “ Construct Child with 1 parameter”; }
Child(int v1,int v2,int v3,int m1,int m2): mem2(m1),
Parent1(v1),Parent3(v3),Parent2 (v2),mem1(m2){
cout <<“ construct child with 3 parameter”; }
~Child(void) { cout << “destruct Child”; }
};
main() {
Member m(16); // Construct Member with 1 parameter
// destruct Member
Child child1; // Construct Parent3 free parameter
// Construct Parent2 free parameter
// Construct Parent1 free parameter
// Construct Member free parameter
// Construct Member free parameter
// construct child free parameter
// destruct Child
// destruct Member
// destruct Member
//destruct Parent1
// destruct Parent2
// destruct Parent3
Child child2(m); // Construct Parent3 free parameter
// Construct Parent2 free parameter
// Construct Parent1 free parameter
// Construct Member free parameter
// Construct Child with 1 parameter
// destruct Child
// destruct Member
// destruct Member
// destruct Parent1
// destruct Parent2
// destruct Parent3
Child child3(10,20,30,40,50); // Construct Parent3 with 1 parameter
// Construct Parent2 free parameter
// Construct Parent1 with 1 parameter
// Construct Member 1 parameter 50
// Construct Member 1 parameter 40
// construct child with 3 parameter
// destruct Child
// destruct Member
// destruct Member
// destruct Parent1
// destruct Parent2
// destruct Parent3
}
Виртуальные базовые классы
В прямом ациклическом графе наследования класс может появиться более чем один раз. Рассмотрим ПАГ множественного наследования, приведенный на рис. Элементы данных (экземпляры переменных) класса Parent появляются дважды в классе GrandChild. Первый набор наследуется через Child1, второй - через Child2. Такое наследование бывает нежелательно. Виртуальные базовые классы обеспечивают механизм для избежания дублирования элементов в классе, таком как CrandChild
Допустим, класс Parent на самом деле называется Vehicle (транспортное средство). В его протоколе содержится закрытое поле данных int topSpeed. Класс Child1 на самом деле называется Boat(Корабль), а класс Child2 -называется Plane (самолет). Наконец, класс GrandChild на самом деле называется SeaPlane. Для SeaPlane желательно наследовать Boat::topSpeed и Plane::topSpeed. Объект класса SeaPlane имеет различную предельную скорость в зависимости от того, действует он как корабль или самолет.
С другой стороны, допустим, класс Parent назван DomesticAnimal (домашнее животное), класс Child1 назван Cow (корова), класс Child2 назван Buffalo (бык), а класс GrandChild назван Beefalo(теленок). Допустим, протокол класса DomesticAnimal включает экземпляры переменных int weight, float price, char color[20]. Нежелательно, чтобы протокол класса Beefalo имел по два экземпляра переменных weight, price, color. Избежать дублирования позволяет использование виртуальных базовых классов.
class DomesticAnimal {
protected:
int weight; float price; char color[20];
public:
DomesticAnimal(void) {weight=0; price=0.; strcpy(color,”none”); }
DomesticAnimal(int aweight, float aprice,char *acolor) {
weight=aweight; price=aprice; strcpy(color,acolor);
}
virtual void print(void) { cout << weight << price << color; }
};
class Cow: public virtual DomesticAnimal {
public:
Cow(void) { }
Cow(int aweight,float aprice,char *acolor) {
weight=aweight; price=aprice; strcpy(color,acolor); }
void print(void) { cout << “ Cow has propeties”; DomesticAnimal::print(); }
};
class Buffalo: public virtual DomesticAnimal {
public:
Buffalo(void) { }
Buffalo(int aweight,float price,char *acolor) {
weight=aweight; price=aprice; strcpy(color,acolor); }
void print(void) { cout<< “ Buffalo has propeties”;
DomesticAnimal::print(); }
};
class Beefalo: public Cow,public Buffalo {
public:
Beefalo(int aweight,float aprice,char *acolor) {
weight=aweight; price=aprice; strcpy(color,acolor); }
void print(void) { cout << “ beefalo has propeties”;
DomesticAnimal::print(); }
};
main() {
Cow aCow(1400,375.0,”black and white”); // void const Domestic
// const par Cow
Beefalo aBeefalo(1700,525.0,”Brown and black”); // void cons Domestic
// void const Cow
// void const Buffalo
// const par Beefalo
DomesticAnimal& myCow=aCow;
DomesticAnimal& myBeefalo=aBeefalo;
my.Cow.print();
myBeefalo.print();
}
Результат работы
Cow has propeties
weight=1400 price=375.0 color=black and white
beefalo has propeties
weight=1700 price=525.0 color=brown and white
Код приведенной выше программы решает проблему класса Beefalo, который наследует поля данных weight, price, color. Объекты класса Beefalo имеют по одному полю данных для веса, цены и цвета.
- Виртуальные базовые классы инициализируются (вызывается void-конструктор) перед любыми не виртуальными базовыми классами и в том порядке, в котором они появляются в ПАГе наследования при просмотре его снизу-вверх и слева направо.
- Если виртуальный базовый класс имеет хотя бы один конструктор, то он должен иметь void-конструктор.
Ключевое слово virtual в классе Cow и классе Buffalo предотвращает многократное копирование полей данных weight,price,color из предков класса Beefalo.
Вопрос:
1. Что, если из объявлений классов Cow и Buffalo убрать ключевое слово virtual.
Компилятор выдаст сообщение об ошибке. В Beefalo унаследовано несколько копий полей weight,price,color, следовательно, конструктор с несколькими параметрами в классе Beefalo становится недействительным.
2. Что, если конструктор класса Cow заменить на
Cow(int aweight,float aprice,char *acolor):
DomesticAnimal(aweight,aprice,acolor) { }
Вместо void конструктора DomesticAnimals вызовется конструктор с параметрами.
3. Что, если конструктор с параметрами класса Beefalo заменить на
Beefalo(int aweight,float aprice,char *acolor) :
DomesticAnimal(aweight,aprice,acolor){}
Все будет работать нормально. Вначале вызовется конструктор базового виртуального класса с параметрами, затем конструкторы наследуемых классов без параметров. Вызова конструктора без параметра виртуального базового класса не будет.
Другой пример:
class W{
public:
virtual void f(void) { cout << “ W::f”; }
virtual void g(void) {cout << “ W::g”; }
virtual void h(void) {cout << “ W::h”; }
virtual void k(void) {cout << “ W::k”; }
};
class A: public virtual W {
public:
void g(void) { cout << “ A::g”;}
};
class B: public virtual W {
public:
void f(void) { cout << “ B::f”; }
};
class C: public A,public B, public virtual W {
public:
void h(void) { cout << “C::h”; }
void f(void) { B::f(); }
void g(void) { A::g(); }
};
main() {
C *pc=new C;
pc->f(); // C::f() ( B::f)
pc->g(); // C::g() ( A::g)
pc->h(); // C::h
((A *)pc)->f(); // C::f (B::f)
((W *)pc)->f(); // C::f (B::f)
B *pb=new B;
pb->f(); // B::f
pb->g(); // W::g
((W *)pb)->f(); // B::f
A *pa=new A;
pa->f(); // W::f
pa->g(); // A::g
((W *)pa)->g(); // A::g
}
При вызове ((A *)pc)->f() выполняется функция f класса С. Тоже происходит и при вызове ((W *)pc)->f().Таблица виртуальных функций использует определение f в классе С. Это демонстрирует то, что при вызове “верхней” виртуальной функции по одному пути ПАГа может закончиться вызовом функции по другому пути.
Вопрос
1. Что, если в Классе А определить функцию член f.
Последняя часть работы программы измениться
A::f A::g A::g
Если виртуальный базовый класс и производный класс разделяют имя какого-либо поля, функции-члена или перечисления, то имя в производном классе скрывает имя в виртуальном базовом классе.
class V { public: int i; void f(); };
class A: virtual public V {public: int i; int f(); };
class B: virtual public V { };
class C: public A, public B {
void g() { i=f(); } // Правильно A::i и A::f() скрывают V::i V::f()
};
Их конструкторы вызываются конструктором последнего класса в цепочке производных классов.
V::V(int n): i(n) {}
A::A(int i): V(i) {}
B::B(int i): V(i) {}
C::C(int i): V(i), A(i), B(i) {}
В объявлении класса могут быть смешаны с невиртуальными базовыми классами. Виртуальные базовые классы могут вызывать нежелательные множественные вызовы функций-членов в виртуальном базовом классе.
ckass V { public: void f(); };
class A: virtual public V {
public:
void f() { V::f(); }
};
class B: virtual public V {
public:
void f() { V::f(); }
};
class C: public A,public B {
public:
void f() { A::f(); B::f(); } // V::f() вызывается дважды
};
Для преодоления указанного эффекта можно определить функцию real_f(), которая выполняет действия, специфичные для функции f() данного класса. Тогда функция f() будет выполнять последовательные вызовы сначала функции real_f(), а затем - функции f().
class V {
protected:
void real_f() {}
public:
void f() { real_f(); }
};
class A: virtual public V {
protected:
void real_f() {}
public:
void f() { real_f(); V::f(); }
};
class B: virtual public V {
protected:
void real_f() { }
public:
void f() { real_f(); V::f(); }
};
class C: public A, public B {
protected:
void real_f() {}
public:
void f() { real_f(); A::reaal_f(); B::real_f(); V::real_f(); }
};
Задания на лабораторные работы по Объектно-ориентированному программированию, множественное наследование
Вариант 1.
Задание 1 .
Создать иерархию типов, описывающую - студента, отца семейства и студента-отца семейства. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую - человека, студента, отца семейства и студента-отца семейства. Использовать виртуальные базовые классы.
Вариант 2.
Задание 1 .
Создать иерархию типов, описывающую работника и отца-семейства, и работника-отца семейства. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2
Создать иерархию типов, описывающую - человека, работника, отца семейства и работника-отца семейства. Использовать виртуальные базовые классы.
Вариант 3.
Задание 1 .
Создать иерархию типов - файл для чтения, файл для записи и файл для чтения и записи. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую файл, файл для чтения, файл для записи и файл для чтения и записи. Использовать виртуальные базовые классы.
Вариант 4.
Задание 1 .
Создать иерархию типов, описывающую работника и женщину, и работника-женщину семейства. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую человека, работника и женщину, и работника-женщину семейства. Использовать виртуальные базовые классы.
Вариант 5.
Задание 1 .
Создать иерархию типов, описывающую операционную систему и прикладное программное обеспечение, и Windows NT как операционную систему и прикладное программное обеспечение. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую программное обеспечение, операционную систему и прикладное программное обеспечение, и Windows NT как операционную систему и прикладное программное обеспечение. Использовать виртуальные базовые классы.
Вариант 6.
Задание 1 .
Создать иерархию типов, описывающую данные - сигнал, данные результат обработки и данные, как результат обработки сигнала и представляющие собой сигнал. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую данные - сигнал, данные результат обработки и данные, как результат обработки сигнала и представляющие собой сигнал. Использовать виртуальные базовые классы.
Вариант 7.
Задание 1 .
Создать иерархию типов - море, залив и бухта. . Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов - соленная вода, море, залив и бухта. Использовать виртуальные базовые классы.
Вариант 8.
Задание 1 .
Создать иерархию типов - корабль, пассажирский транспорт и пассажирский корабль. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов - транспорт, корабль, пассажирский транспорт и пассажирский корабль. Использовать виртуальные базовые классы.
Вариант 9.
Задание 1 .
Создать иерархию типов - машина, пассажирский транспорт и автобус. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов - машина, пассажирский транспорт и автобус. Использовать виртуальные базовые классы.
Вариант 10.
Задание 1 .
Создать иерархию типов, описывающую - студента, отца семейства и студента-отца семейства. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую - человека, студента, отца семейства и студента-отца семейства. Использовать виртуальные базовые классы.
Вариант 11.
Задание 1 .
Создать иерархию типов, описывающую работника и отца-семейства, и работника-отца семейства. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2
Создать иерархию типов, описывающую - человека, работника, отца семейства и работника-отца семейства. Использовать виртуальные базовые классы.
Вариант 12.
Задание 1 .
Создать иерархию типов - файл для чтения, файл для записи и файл для чтения и записи. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую файл, файл для чтения, файл для записи и файл для чтения и записи. Использовать виртуальные базовые классы.
Вариант 13.
Задание 1 .
Создать иерархию типов - корабль, пассажирский транспорт и пассажирский корабль. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов - транспорт, корабль, пассажирский транспорт и пассажирский корабль. Использовать виртуальные базовые классы.
Вариант 14.
Задание 1 .
Создать иерархию типов, описывающую операционную систему и прикладное программное обеспечение, и Unix как операционную систему и прикладное программное обеспечение. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.
Задание 2.
Создать иерархию типов, описывающую программное обеспечение, операционную систему и прикладное программное обеспечение, и Unix как операционную систему и прикладное программное обеспечение. Использовать виртуальные базовые классы.
ЭКЗАМЕНАЦИОННЫЕ ВОПРОСЫ
1. Общая характеристика языка С и его характерные отличия от языка PASCAL.
2. Структура программы на языке С. Подключение библиотек, Организация ввода-вывода с помощью библиотеки iostream.
3. Модуль в языке С. Организация многофайловой программы, заголовочные файлы, директива #include, прототипы функций.
4. Оператор присваивания и оператор ветвления. Особенности оператора присваивания и вычисления условий в языке С. Примеры операторов присваивания и ветвления.
5. Объявление массивов с языке С. Организация циклов. Цикл с предусловием и постусловием. Цикл for и его связь с циклом while. Примеры.
6. Оператор переключатель. Инструкция break. Примеры применения оператора переключателя.
7. Числовые типы данных языка С. Целочисленные (десятичные, восьми и шестнадцатиричные) и действительные константы. Именованные константы и способы их задания в языке С.
8. Перечисленный тип в языке С. Особенности, возможности и примеры его использования.
9. Понятие блока в языке С. Объявления и инициализация переменных и массивов в С.
10. Обзор операций языка С. Приоритеты операций. Операции низкого и высокого уровней. Примеры операций.
11. Выражения в языке С. Правила вычисления выражений. Приоритеты операций и правило ассоциативности. Приведение типов в С и С++. Операция последования (скобки).
12. Символьный тип в языке С. Символьные константы. Представление строк в С. Библиотека string. Примеры функций библиотеки string.
13. Указатели и ссылки в языке С. Константные указатели и ссылки. Операции с указателями и ссылками.
14. Массив как указатель в языке С. Особенности индексации массива при записи кода. Массив как параметр функции. Многомерные массивы.
15. Функции в языке С. Объявление, описание и вызов. Особенности передачи параметров (по значению, ссылке, указателем, константной ссылке). Перегрузка функций и параметры по умолчанию.
16. Функции в языке С. Встраиваемые функции и макросы. Передача функции как параметр. Функция как указатель на функцию. Массив указателей на функции.
17. Структуры в языке С. Объявление типа и переменных. Использование элементов структур. Вложенные структуры и массивы. Примеры построения вложенных структур и массивов.
18. Объединения в языке С. Объявление типа и переменных. Использование элементов объединений. Битовые поля.
19. Классы и типы памяти. Время жизни и область видимости объектов. Локальные и глобальные объекты. Механизм управления стеком.
20. Классы и типы памяти. Время жизни и область видимости объектов. Локальные и глобальные объекты. Механизм управления кучей.
21. Форматный вывод и ввод. Функции printf и scanf библиотеки stdio.
22. Форматные преобразования в памяти. Функции sprintf и sscanf библиотеки stdio.
23. Работа с текстовыми файлами. Функции fopen, fclose, fprintf, fscanf библиотеки stdio.
24. Работа с записеориентированными файлами. Функции fopen, fclose, fprintf, fscanf библиотеки stdio.
25. Работа с бинарными файлами. Функции fopen, fclose, fwrite, fread, fseek библиотеки stdio.
26. Технологические основы языков программирования высокого уровня. Структурное и модульное программирование.
27. Технологические основы языков программирования высокого уровня. Простые и сложные задачи. Причины возникновения сложности. Решение проблемы сложности задач.
28. ООП и объектная модель. Основные принципы объектной модели.
29. Анализ, проектирование и программирование. Объектный подход, алгоритмическая и объектно-ориентированная декомпозиция. Достоинства и недостатки ООП.
30. Абстрактные типы данных. Понятие класса. Инкапсуляция. Объявление класса. Поля и методы. Секции public и private. Скрытие данных.
31. Объявление класса. Поля и методы. Секции public и private. Скрытие данных. Пример разработки класса “Комплексное число”. Реализация методов класса. Встроенные методы.
32. Константные поля. Константные методы. Использование константных ссылок в списке параметров методов класса. Пример разработки класса “Комплексное число”.
33. Способы создания объектов. Доступ к данным и методам объекта. Указатель this.
34. Статическое и динамическое создание объектов внутри методов. Ссылки в заголовках методов. Примеры.
35. Создание объектов. Конструкторы. Виды конструкторов.
36. Конструктор копирования. Пример разработки конструктора копирования.
37. Уничтожение объектов. Деструкторы.
38. Статические поля и методы класса. Дружественные функции и классы.
39. Пример разработки класса “Вектор”. Реализация операций без использования перегрузки.
40. Перегрузка операций. Особенности перегрузки для унарных, бинарных операций. Перегрузка операции [].
41. Перегрузка операций. Перегрузка операции присваивания. Операция присваивания и конструктор копирования.
42. Суть проблемы моделирования. Инкапсуляция, модульность и иерархия как средства решения проблемы. Виды иерархий. Наследование и агрегация. Реализация агрегации в C++. Пример.
43. Наследование в C++. Простое (одиночное) наследование. Public, private и protected-наследование. Некоторые правила наследования.
44. Наследование в C++. Простое (одиночное) наследование. Пример последовательного проектирования иерархии классов геометрических фигур. Перекрытие (замещение) методов.
45. Раннее и позднее связывание. Механизм виртуальных методов.
46. Механизм виртуальных методов. Применение виртуальных методов для разработки классов “Точка” и “Круг”.
47. Чисто виртуальные методы и абстрактные классы. Абстрактный класс в примере “Геометрические фигуры”.
48. Множественное наследование. Виртуальное наследование.
49. Перегрузка функций в C++. Сигнатура. Алгоритм проверки соответствия сигнатуре.
50. Шаблоны функций. Шаблоны классов. Инстанцирование и специализация.