
- •220300 - Системы автоматизированного проектирования
- •Тема 2. Технологии программирования
- •Тема 2. Технология разработки крупных приложений
- •Структуры
- •Структуры и функции
- •Массивы структур
- •Поиск в массиве структур
- •Вложенность структур
- •Рекурсия
- •Алгоритм быстрой сортировки
- •Массивы структур и бинарные файлы
- •Динамические структуры данных
- •Линейные списки
- •Очереди
- •Контрольная работа
- •Объектно-ориентированное программирование. Классы
- •Конструкторы
- •Перегруженные конструкторы
- •Определение методов класса вне класса
- •Объекты, возвращаемые функцией (методом)
- •Структуры и классы
- •Классы и память
- •Статические данные класса
- •Константные методы
- •Деструкторы
- •Массивы и классы
- •Массивы объектов
- •Строки Строковый тип или стандартный класс string
- •Тип строк AnsiString
- •Перегрузка операций
- •Перегрузка арифметических операций
- •Перегрузка операций сравнения
- •Перегрузка операции приведения типа
- •Преобразования объектов в основные типы и наоборот
- •Преобразование объектов классов в объекты других классов
- •Наследование
- •Конструкторы производного класса
- •Перегрузка функций
- •Иерархия классов
- •Общее и частное наследование. Комбинации доступа
- •Множественное наследование
- •Включение. Классы в классах
- •Виртуальные и дружественные функции
- •Абстрактные классы и чистые виртуальные функции
- •Виртуальные деструкторы
- •Виртуальные базовые классы или устранение неоднозначности при множественном наследовании
- •Дружественные функции
- •Дружественные классы
- •Указатель this
- •Многофайловые программы
- •Распознавание нажатых клавиш
Абстрактные классы и чистые виртуальные функции
Иногда, разрабатывая иерархию классов, можно получить базовый класс, для которого создание объекта как бы теряет смысл. В предыдущей программе это хорошо прослеживается. Классы, для которых нет смысла создавать объекты, объявляют как абстрактные. Базовый класс, объекты которого никогда не будут реализованы, называется абстрактным классом. Такой класс может существовать с единственной целью – быть родительским по отношению к производным классам, объекты которых будут реализованы.
Абстрактный класс содержит хотя бы один чистый виртуальный метод, т.е. метод, объявленный в классе, но не имеющий конкретной реализации. Синтаксис объявления чистой виртуальной функции дополняется конструкцией =0.
class Base { //абстрактный Базовый класс
public:
virtual void show()=0; //чистая Виртуальная функция
//{ cout << "Base\n"; } уже не надо!!!
};
… тоже самое
int main() {
// Base b; //невозможно создание объекта абстрактного класса
… тоже самое
}
В результате, программа выведет:
Derv1
Derv2
Здесь, конструкция =0 – это просто способ сообщить компилятору, что функция будет чистой виртуальной. Если в классе используется чистая виртуальная функция, то он становится абстрактным и никакие объекты из него реализовать не удастся. Компилятор не допустит создания объекта для абстрактного класса.
Виртуальные деструкторы
Допустим, чтобы удалить объект производного класса, вы выполнили delete над указателем базового класса, указывающим на порождённый класс. Если деструктор базового класса не является виртуальным, тогда delete, будучи обычным методом, вызовет деструктор для базового класса вместо того, чтобы запустить деструктор для производного класса.
class Base {
public:
~Base() //невиртуальный деструктор
// virtual ~Base() //виртуальный деструктор
{ cout << "Base udalen\n"; }
};
//---------------------------------------------------------------------------
class Derv : public Base {
public:
~Derv()
{ cout << "Derv udalen\n"; }
};
//---------------------------------------------------------------------------
int main() {
Base* pBase = new Derv;
delete pBase;
getch(); return 0;
}
Программа выдаёт такой результат: Base udalen . Это говорит о том, что деструктор производного класса не вызывается вообще! Если написать virtual ~Base(), то обе части объекта порождённого класса будут удалены корректно и программа выведет: Derv udalen; Base udalen.
Виртуальные базовые классы или устранение неоднозначности при множественном наследовании
Рассмотрим следующую ситуацию.
Parent
Child1
Child2
Vnuk
class Parent {
protected:
int basedata;
};
class Child1 : public Parent
{ };
class Child2 : public Parent
{ };
class Vnuk : public Child1, public Child2 {
public:
int getdata()
{ return basedata; } // ОШИБКА: неоднозначность
};
Здесь, при попытке метода int getdata() получить доступ к переменной базового класса basedata возникнет ошибка. Дело в том, что каждый из порождённых классов (Child1 и Child2) наследует свою копию базового класса Parent. Это копия называется подобъектом. Каждый из двух подобъектов содержит собственную копию данных базового класса, включая basedata. Здесь и возникает неоднозначная ситуация. Для устранения того рода неоднозначности классы Child1 и Child2 необходимо сделать наследниками виртуального базового класса, что приведет к созданию только одной общей копии базового класса Parent.
… тоже самое
class Child1 : virtual public Parent
{ };
class Child2 : virtual public Parent
… тоже самое
Приведём более конкретный пример.
class A {
protected:
int x;
public:
A() { x=0; }
A(int xx) { x=xx; }
};
//---------------------------------------------------------------------------
class B : virtual public A {
public:
void addB(int y) { x=x+y; }
};
//---------------------------------------------------------------------------
class C : virtual public A {
public:
void addC(int y) { x=x*y; }
};
//---------------------------------------------------------------------------
class Vnuk : public B, public C {
public:
void show() { cout << "X= " << x << endl; } //если-бы не virtual, то здесь возникла бы неоднозначность
};
//---------------------------------------------------------------------------
int main() {
Vnuk v1;
v1.show(); // 0
v1.addB(10); v1.show(); // 10
v1.addC(5); v1.show(); // 50
getch(); return 0;
}
Добавив спецификатор virtual перед модификаторами доступа к базовому классу А в объявлениях классов В и С, мы включили в класс Vnuk только одну копию класса А, тем самым устранили проблему неоднозначности.