yandex rtb 1
ГоловнаЗворотній зв'язок
yande share

Объектно-ориентированное программирование

Лекция 8.

Наследование. Открытое и закрытое наследование.

 

Наследование – это механизм, позволяющий получить новый класс на основе существующего. Существующий класс может быть изменен или дополнен для создания нового класса. Класс, на основе которого создается новый класс, называется базовым (суперклассом). Наследуемый класс называется производным (или подклассом).

Буч определяет наследование следующим образом:

Наследование - это такое отношение между классами, когда один класс повторяет структуру и поведение другого класса (одиночное наследование) или других (множественное наследование) классов.

Наследование есть процесс, с помощью которого один объект приобретает свойства другого объекта. При использовании наследования объект нуждается в определении только тех качеств, которые делают его уникальным в собственном классе. Он может наследовать общие свойства от своего родителя. Поэтому именно механизм наследования дает возможность одному объекту быть специфическим экземпляром общего случая. Наследование взаимодействует также с инкапсуляцией. Если данный класс инкапсулирует некоторые атрибуты, то любой подкласс будет иметь те же атрибуты плюс атрибут, который он добавляет как часть своей специализации. Эта ключевая концепция, которая позволяет объектно-ориентированным программам расти по сложности линейно, а не в геометрически. Новый подкласс наследует все атрибуты всех его предков. Он не имеет непредсказуемых взаимодействий с большинством остальных кодов в системе.

Класс можно сделать производным от существующего с использованием следующей формы:

 

class имя_класса : (public | protected | private) имя_базового_класса {

объявления членов класса

};

 

Ключевые слова public, protected, private используются для указания того, насколько члены базового класса будут доступны из производного. Использование в заголовке производного класса public означает, защищенные и открытые члены базового класса должны наследоваться как защищенные и открытые члены производного класса. Это означает, что и защищенные (protected), и открытые (public) члены базового класса доступны в производном классе и также являются защищенными и закрытыми. Закрытые (private) члены базового класса недоступны для производного класса. Заметим, что закрытые и защищенные члены базового класса недоступны из классов, не являющихся производными базового. Открытое наследование, называемое также интерфейсным наследованием, означает, что производный тип является подтипом базового (отношение ISA). Каждый объект производного класса является объектом базового типа.

Например, класс млекопитающих является базовым для класса собак, класс собак – базовым для класса болонок. Т.е. каждая болонка – собака, каждая собака – млекопитающее, но не наоборот.

 

Пример открытого одиночного наследования:

 

#include <iostream.h>

class rectangle {

protected:

   float x,y,height,width;

public:

   rectangle(float xbase, float ybase, float h, float w):

     x(xbase), y(ybase), height(h), width(w) { }

     float area() { return height*width; }

   void printarea() {

               cout<<"rectangle area="<<area()<<endl;     

   }

};

class colorsquare : public rectangle {

private:

   long color;

public:

   colorsquare(float xbase, float ybase, float h, long c):

     rectangle(xbase,ybase,h,h), color(c) { }

     long getcolor() {return color;}

     void printarea() {

                 cout<<"square area="<<area()<<endl;

     }

};

void main() {

   rectangle a(0,0,10,20),*refa;

   refa=&a;

   refa->printarea();

   colorsquare b(0,0,10,0xffffffff), *refb;

   refb=&b;

   refb->printarea();

   refa=refb;

   refa->printarea();

   //refa->getcolor();

}

 

Результат работы программы:

 

rectangle area=200

square area=100

rectangle area=100

 

В этом примере мы создаем базовый класс rectangle, имеющий защищенные члены класса, определяющие параметры прямоугольника. Заметим, что мы объявили их защищенными, а не закрытыми именно с той целью, чтобы они были доступны для производного класса и не доступны извне. Класс colorsquare (цветной квадрат) является подтипом базового класса. Это означает, что производный класс наследует все доступные (открытые и защищенные) свойства и методы базового класса. В дополнение производный класс добавлено свойство color и метод, позволяющий с этим свойством работать. В примере видно, что конструктор базового класса используется конструктором производного класса как часть списка инициализации. Это понятно, ведь объект класса colorsquare является одновременно объектом класса rectangle.

В производном классе мы можем переопределить методы базового класса. В качестве примера мы переопределяем функцию printarea – для разных классов эта функция выдает разные сообщения.

При открытом наследовании указатель на базовый класс может быть также указателем на производный класс. В программе мы создаем два указателя – один на базовый класс rectangle и другой – на производный класс colorsquare. Правило преобразования указателей состоит в том, что указатель на открытый производный класс может быть неявно преобразован к указателю на его базовый класс. Присваивая указатель на производный класс указателю на базовый класс, не возникает ошибки. Компилятор связывает вызов функции с тем ее вариантом, который отвечает классу, указанному при объявлении указателя, а не тому, на объект которого в данный момент направлен указатель. Фактически нам становится доступен объект базового класса, образованный в процессе создания объекта производного класса. Заметим, что методы и свойства производного класса с помощью такого указателя недоступны.

В дальнейшем будет показано, что необходимо сделать для того, чтобы выбор вызываемой функции определялся на этапе выполнения в зависимости от того, на что направлен указатель.

Язык C++ позволяет создавать классы одновременно на основе нескольких базовых классов (множественное наследование). Заметим, что это позволяют не все объектно-ориентированные языки программирования. Например, Java не позволяет использовать множественное наследование.

В случае множественного наследования класс определяется следующим образом:

class имя_класса : (public | protected | private) имя_базового_класса1, (public | protected | private) имя_базового_класса2, … {

объявления членов класса

};

Использование множественного наследования может создавать неоднозначности. Например, можно представить себе следующую ситуацию: Class1 является базовым для классов Class11 и Class12. Они в свою очередь являются базовыми для класса Class2.

Предположим теперь, что в классах Class11 и Class12 переопределяется некоторый метод method1 базового класса Class1. Возникает вопрос, какой же экземпляр метода будет использоваться в классе Class2? Решить эту проблему можно, добавляя в качестве префиксов имя класса-источника: Class12::method1.

Закрытое наследование не носит характера отношения подтипов. При закрытом наследовании мы повторно используем код базового класса, но не предполагаем рассматривать объекты производного класса как объекты базового. Закрытое наследование называется отношением LIKEA, или наследованием реализации. Закрытое и защищенное наследование не создает иерархии типов. Поэтому, если класс colorsquare будет закрытым наследником класса rectangle, преобразования указателей невозможны. В основном такое наследование используется для повторного использования кода.

Практически закрытое наследование не используется.

 

10