Состав сооружений: решетки и песколовки: Решетки – это первое устройство в схеме очистных сооружений. Они представляют...
Опора деревянной одностоечной и способы укрепление угловых опор: Опоры ВЛ - конструкции, предназначенные для поддерживания проводов на необходимой высоте над землей, водой...
Топ:
Оснащения врачебно-сестринской бригады.
Комплексной системы оценки состояния охраны труда на производственном объекте (КСОТ-П): Цели и задачи Комплексной системы оценки состояния охраны труда и определению факторов рисков по охране труда...
Проблема типологии научных революций: Глобальные научные революции и типы научной рациональности...
Интересное:
Влияние предпринимательской среды на эффективное функционирование предприятия: Предпринимательская среда – это совокупность внешних и внутренних факторов, оказывающих влияние на функционирование фирмы...
Финансовый рынок и его значение в управлении денежными потоками на современном этапе: любому предприятию для расширения производства и увеличения прибыли нужны...
Инженерная защита территорий, зданий и сооружений от опасных геологических процессов: Изучение оползневых явлений, оценка устойчивости склонов и проектирование противооползневых сооружений — актуальнейшие задачи, стоящие перед отечественными...
Дисциплины:
|
из
5.00
|
Заказать работу |
Содержание книги
Поиск на нашем сайте
|
|
|
|
Расширим пример текстового файла. Предположим, что нам нужно сделать для класса TextFile базовый класс File, от которого будет унаследован еще один класс RTFFile. Однако, в такой ситуации неизвестно как реализовать метод read() класса File, т.к. класс File не реализует поведение какого-то конкретного типа файлов, а представляет интерфейс для работы с различными файлами. В этом случае, метод read(...) этого класса нужно сделать чистым виртуальным, дописав "= 0" после его сигнатуры:
struct FileЭто означает, что метод read(...) должен быть определен в классах наследниках. Теперь класс File стал абстрактным, и его экземпляры невозможно создать. Но зато можно работать через указатель на абстрактный класс с объектами производных классов, например, так:
File *f = new TextFile("text.txt");Следует отметить, что в любой иерархии классов деструктор всегда должен быть виртуальным. Рассмотрим пример, поясняющий важность этого факта:
struct Personstruct Student: Person
{
public:
Student()
{
someData = new Data();
}
~Student()
{
delete someData;
}
//...
private:
Data *someData;
};
//...
Student *s = new Student();
//...
delete s; //вызовется деструктор класса Student, память по указателю someData освободится
Person *p = new Student();
//...
delete p;
/*вызовется деструктор класса Person, а не Student,
т.к. он не является виртуальным, несмотря на то, что на самом деле объект - экземпляр Student.
В этом случае произойдет утечка памяти, т.к. память по указателю someData не освободится
*/
Деструктор можно также сделать чистым виртуальным, но при этом его тело нужно определить снаружи класса.
Таблица виртуальных функций (Virtual Function Table)
Для каждого класса, содержащего виртуальные методы, или унаследованного от класса с виртуальными методами, создается таблица виртуальных функций. Эта таблица предназначена для вызова нужных реализаций виртуальных методов во время исполнения программы. При создании экземпляра класса, указатель на VFT этого класса помещается в самое начало созданного объекта.
Как известно, конструирование объекта происходит поэтапно и начинается созданием объекта самого первого класса в иерархии наследования. Во время этого процесса перед вызовом конструктора каждого класса указатель на VFT устанавливается равным указателю на VFT текущего конструируемого класса. Например, у нас есть 3 класса: A, B, C (B наследуется от A, C наследуется от B). При создании экземпляра С, произойдут 3 последовательных вызова конструкторов: сначала A(), затем B(), и в конце C(). Перед вызовом конструктора A() указатель на VFT будет указывать на таблицу класса A, перед вызовом B() он станет указывать на таблицу класса B() и т.д. Аналогичная ситуация при вызове деструкторов, только указатель будет менятся от таблицы самого младшего класса к самому старшему.
Из этого факта следует правило: в конструкторах и деструкторах нельзя вызывать виртуальные методы. Посмотрим, что произойдет, если нарушить это правило:
struct Astruct B: A
{
B()
{
f();
}
virtual void f()
{
cout << "B::f()" << endl;
}
};
struct C: B
{
C()
{
f();
}
virtual void f()
{
cout << "C::f()" << endl;
}
};
//...
C c; //создание объекта класса C
В этом примере на экран будет выведено:
A::f()B::f()C::f()Модификаторы доступа
В C++ существует три модификатора доступа к членам классов и структур:
Эти же модификаторы можно указывать при наследовании:
struct B: public A {...};Если модификатор не указан, то по умолчанию для структур наследование public, для классов private.
public -наследование позволяет установить между классами отношение ЯВЛЯЕТСЯ. Т.е., если класс B открыто унаследован от A, то объект класса B ЯВЛЯЕТСЯ объектом класса A, но не наоборот.
private наследование выражает отношение РЕАЛИЗОВАНО_ПОСРЕДСТВОМ. Если класс B закрыто унаследован от A, то можно говорить, что объект класса B реализован посредством объекта класса A. В большинстве случаев закрытое наследование можно заменить агрегацией.
Следует отметить тот факт, что закрытый(private) виртуальный метод базового класса нельзя вызвать из наследника, но без проблем можно переопределить.
Ковариантность
Пусть имеется класс A, и класс B - его наследник. Тогда возможно определить классы C и D, с виртуальным методом f() следующим образом:
struct A {...};struct C
{
virtual A * f();
};
struct D: C
{
virtual B * f();
}
Такая возможность в C++ называет ковариантностью по типу возвращаемого значения (return type covariance).
|
|
|
История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...
Папиллярные узоры пальцев рук - маркер спортивных способностей: дерматоглифические признаки формируются на 3-5 месяце беременности, не изменяются в течение жизни...
Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...
История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...
© cyberpediasu.com 2017-2026 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!