
- •Глава 13. Включение, вложение и наследование классов
- •13.1. Включение объектов классов
- •13.2. Вложение классов
- •13.3. Наследование классов
- •13.4. Доступность членов класса при наследовании
- •13.5. Методы при наследовании
- •13.6. Абстрактные методы и абстрактные классы.
- •13.7. Опечатанные классы и методы
- •13.8. Применение абстрактых классов
13.3. Наследование классов
Наиболее богатым в отношении повторного использования кода и полиморфизма является отношение наследования классов. Для него используют название "является" (is-a). При наследовании объект производного класса служит частным случаем или специализацией объектабазового класса. Например, велосипед является частным случаем транспортного средства. Не проводя сравнительного анализа всех тонкостей возможных отношений между классами (этому нужно заниматься в специальном курсе, посвященном объектно-ориентированной методологии, см., например, [3]), покажем на примере рассматриваемых классов "точка на плоскости" и "окружность с заданным точкой центром" как реализуется наследование в языке С#. Итак класс Point будет базовым классом, а класс Circle сделаем его наследником иначе говоря, производным от него.
Для объявления класса, который является наследником некоторого базового класса, используется следующий синтаксис:
модификаторыоpt class имя_производного_класса : имя_базового_класса {операторы_тела_производного_класса}
Конструкция :имя_базового_класса в стандарте С# называется спецификацией базы класса.
Таким образом, класс Circle как производный от базового класса Point можно определить таким образом (программа 13_04.cs):
class Circle : Point // класс "окружность на плоскости"
{ // Закрытое поле:
double rad; // радиус окружности
// свойство для радиуса окружности:
public double Rad { get { return rad; } set { rad = value; } }
// свойство для получения значения длины окружности:
public double Len { get { return 2 * rad * Math.PI; } }
// свойство для центра окружности:
public Point Centre
{
get
{
Point temp = new Point();
temp.X = X;
temp.Y = Y;
return temp;
}
set
{
X = value.X;
Y = value.Y;
}
}
public void display()
{
Console.WriteLine("Centre: X={0}, Y={1}; Radius={2}, Length={3,6:f2}", X, Y, rad, Len);
}
}
В производном классе Circle явно определены поле double rad, три уже рассмотренных свойства Rad, Len Centre и метод display(). По сравнению с предыдущими примерами класс Point не изменился. Он так же содержит два закрытых поля, задающих координаты точки, и два открытых свойства X, Y,обеспечивающие доступ к этим полям. В классе Point конструктор добавлен компилятором. Нет явного определения конструктора и в классе Circle. Поэтому объекты класса Circle можно создавать только с умалчиваемыми значениями полей.
При наследовании производный класс "получает в наследство" все поля, свойства и методы базового класса, за исключением конструктора – конструктор базового класса не наследуется. Получив от базового класса его поля, методы и свойства, базовый класс может по-разному "распорядиться с наследством". Поля базового класса непосредственно входят в число полей производного класса. Однако доступ к полям базового класса для методов, свойств и объектов производного класса разрешен не всегда. Закрытые поля свойства и методы базового класса недоступны для методов, свойств и объектов производного класса.
Открытые поля, методы, и свойства базового класса доступны для методов, свойств и объектов производного класса. В нашем примере класс Point имеет два открытых свойства, которыми можно пользоваться как внутри класса Circle так и во внешнем мире, обращаясь к этим свойствам с помощью объектов класса circle. В методе display() производного класса выполняется непосредственное обращение к унаследованным свойствам X, Y.
Особое внимание в нашем примере с наследованием нужно обратить на свойство Circle.Centre. При включении и вложении класса Point в класс Circle значением этого свойства служит ссылка на непосредственно существующий объект класса Point. В случае наследования в объекте класса Circle объекта класса Point нет - присутствуют только поля такого объекта и в классе Circle доступны открытые свойства класса Point. Поэтому для объявления в классе Circle свойства Centre объект класса Point приходится "реконструировать". В get-аксессоре явно создаётся временный объект класса Point, его полям присваиваются значения полей, унаследованных классом Circle от базового класса Point. Ссылка на этот временный объект возвращается как значение свойства Circle.Centre. Set-аксессор свойства Circle.Centre очень прост -используются унаследованные свойства X, Y класса Point.
Следующий фрагмент программы (см. 13_04.cs) демонстрирует возможности класса Circle,
public static void Main()
{
Circle rim = new Circle();
rim.X = 24;
rim.Y = 10;
rim.Rad = 2;
rim.display();
rim = new Circle();
rim.display();
}
В методе Main() создан объект класса Circle. Он ассоциирован со ссылкой rim, и с ее помощью осуществляется доступ как к свойствам и методам объекта класса Circle, так и к свойствам объекта базового класса Point.
Следующие результаты выполнения программы дополняют приведенные объяснения:
Centre: X=24, Y=10; Radius=2, Length= 12,57 Centre: X=0, Y=0; Radius=0, Length= 0,00