- •1. Объектно-ориентированный подход в программировании.
- •1.1 Инкапсуляция
- •1.2 Наследование
- •1.3 Полиморфизм
- •2.1 Перегружаемые (overload) функции
- •2.2 Перегружаемые (overload) операторы
- •2.3 Объектные типы данных: структуры, объединения, классы (обзор)
- •2.4. Конструкторы и деструкторы
- •2.8 Встраиваемые функции
- •2.7 Производные классы
- •2.9 Присваивание объектов
- •2.10 Передача в функции и возвращение объекта
- •2.11 Конструктор копирования
- •2.12 Указатели и ссылки на объекты
- •3.1. Модификаторы наследования
- •3.2 Конструкторы при наследовании
- •3.3 Деструкторы при наследовании
- •3.4 Совместимость типов
- •4. Виртуальные функции.
- •4.1. Раннее и позднее связывание
- •4.2 Полиморфизм и виртуальные методы
- •4.3. Использование указателей на базовые классы при адресации объектов произвольных классов.
- •4.4 Абстрактный класс
- •5. Дружественные функции.
- •5.1 “Дружественные” (friend) функции
- •6. Шаблоны функций и классов
- •6.1 Шаблоны функций
- •6.2 Шаблоны классов
2.7 Производные классы
Наследование, являясь одной из наиболее интересных составляющих концепции объектно-ориентированного программирования, представляет собой механизм , посредством которого одни классы могут порождать другие, «делегируя» последним часть своих свойств и методов. В результате наследование позволяет строить иерархию классов, переходя от более общих к более специальным.
Когда один класс наследуется другим, класс, который наследуется, называется базовым классом. Наследующий класс называют производным классом.
Новый класс строится на базе уже существующего с помощью конструкции следующего вида.
class Parent {....};
class Child : [модификатор наследования] Parent {....};
При определении класса-потомка за его именем следует разделитель-двоеточие («:»), затем - необязательный модификатор наследования и имя родительского класса. Модификатор наследования определяет видимость наследуемых переменных и методов для пользователей и возможных потом ков самого класса-потомка. Другим и словами, он определяет, какие права доступа к переменным и методам класса-родителя будут переданы классу-потомку. При реализации наследования область видимости принадлежащих классу данных и методов можно определять выбором ключевого слова private (собственный), public (общедоступный) или protected (защищенный), которые могут произвольно чередоваться внутри описания класса. С двумя первыми модификаторами доступа мы уже знакомы - private описывает закрытые члены класса, доступ к которым имеют только методы-члены этого класса, public предназначен для описания общедоступных элементов, доступ к которым возможен из любого места в программе. Особый интерес представляют элементы, обладающие модификатором доступа protected.
Модификатор protected используется тогда, когда необходимо, чтобы некоторые ч лены базового класса оставались закрытыми, но были бы доступны для производного класса. Модификатор protected эквивалентен private с единственным исключением : защищенные ч лены базового класса доступны для ч ленов всех производных классов этого базового класса. То, как изменяется доступ к элементам базового класса из методов производного класса в зависимости от значения модификаторов наследования, можно видеть в следующей таблице.
2.8 Встраиваемые функции
Рассмотрим пример:
struct _3d
{
double x, y, z;
double mod()
{return sqrt (x*x + y*y +z*z);}
double projection(_3d r)
{return (x*r.x + y*r.y + z*r.z) / mod();}
_3d operator + (_3d b);
};
_3d _3d::operator + (_3d b)
{
_3d c;
c.x = x + b.x;
c.y = y + b.y;
c.z = z + b.z;
return c;
}
Обратим внимание на то, в каком месте описывается тело того или иного метода. Методы mod() и projection() описаны вместе со своими телами непосредственно внутри структуры. Однако можно поступить иначе: поместить прототипы метода внутрь структуры, а определение тела функции – вне структуры, как мы поступили с оператором « +». Первый способ используется для простых и коротких методов, которые в дальнейшем не предполагается изменять. Так поступают отчасти из-за того, что описания классов помещают обычно в файлы заголовков, включаемые затем в прикладную программу с помощью директивы #include. Кроме того, при этом способе машинные инструкции, генерируемые компилятором при обращении к этим функциям, непосредственно вставляются в оттранслированный текст. Это снижает затраты на их исполнение, поскольку выполнение таких методов не связано с вызовом функций и механизмом возврата, увеличивая в свою очередь размер исполняемого кода (то есть такие методы становятся встраиваемыми). Второй способ предпочтительнее для сложных методов. Объявленные таким об разом функции автоматически заменяются компилятором на вызовы подпрограмм , хотя при добавлении ключевого слова inline могут подставляться в текст, как и в первом случае.
Кроме представленного выше способа создания встраиваемых функций (записать тело метода непосредственно в структуре), есть еще один способ :
вставить спецификатор inline перед определением метода.
inline _3d _3d::operator + (_3d b)
{
_3d c;
c.x = x + b.x;
c.y = y + b.y;
c.z = z + b.z;
return c;
}
Теперь оператор « +» станет встраиваемой функцией.
Встраиваемые функции действуют почти так же, как и макроопределения с параметрами, однако имеют ряд преимуществ. Во-первых, inline методы обеспечивают более стройный способ встраивания в программу короткой функции. Во-вторых, компилятор C++ гораздо лучше работает со встраиваемыми функциями, чем с макроопределениями. Важно понимать, что inline не является командой для компилятора, это, скорее, просьба сделать метод встраиваемым. Если по каким-то причинам (например, при наличии в теле функции операторов цикла, switch или goto) компилятор не выполнит запрос, то функция будет откомпилирована как не встраиваемая.
