Наследование


Предыдущая страница
Следующая страница  

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

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

Идея наследования реализует отношение "B является A". Например, млекопитающее ЯВЛЯЕТСЯ животным, собака ЯВЛЯЕТСЯ млекопитающим, следовательно, собака ЯВЛЯЕТСЯ животным и так далее.

Базовые классы и производные классы в D

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

class наследуемый_класс: базовый_класс, интерфейс1, интерфейс2

Рассмотрим базовый класс Shape и его производный класс Rectangle:

import std.stdio;

// Базовый класс 
class Shape { 
   public: 
      void setWidth(int w) { 
         width = w; 
      }

      void setHeight(int h) { 
         height = h; 
      }
   
   protected: 
      int width; 
      int height; 
}
  
// Производный класс 
class Rectangle: Shape { 
   public: 
      int getArea() { 
         return (width * height); 
      } 
}
  
void main() { 
   Rectangle Rect = new Rectangle();
   
   Rect.setWidth(5); 
   Rect.setHeight(7); 
   
   // Вывод площади объекта. 
   writeln("Общая площадь: ", Rect.getArea()); 
} 

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

Общая площадь: 35

Контроль доступа и наследование

Производный класс может получить доступ ко всем не-закрытым членам своего базового класса. Таким образом, члены базового класса, которые не должны быть доступны для функций-членов производных классов, должны быть объявлены закрытыми (private) в базовом классе.

Производный класс наследует все методы базового класса со следующими исключениями:

Замечание по поводу наследования перегруженных операторов (методов opUnary, opBinary и пр.). Я попробовал использовать класс Rectangle, унаследованный от класса Shape (см. пример выше) с перегруженным оператором "-" (унарный минус). Сам оператор работал, т.е. формально его наследование работает. Но результатом таких операторов обычно должен быть объект того же типа, как и тот, к которому применяется оператор. Просто это сделать не получается – если функция opUnary объявлена в классе Shape, то она и возвращает объект класса Shape, в то время как если мы применяет унарный минус к объекту Rectangle, то нам нужно возвращать объект класса Rectangle. Возможно, как-то эта проблема решается шаблонной магией, но без неё можно считать, что перегруженные операторы не наследуются – прим. пер.

Многоуровневое наследование

Наследование может быть с большим количеством уровней, как показано в следующем примере.

import std.stdio;

// Базовый класс 
class Shape {
   public:
      void setWidth(int w) {
         width = w; 
      }

      void setHeight(int h) {
         height = h; 
      }

   protected: 
      int width; 
      int height; 
}

// Производный класс 
class Rectangle: Shape {
   public:
      int getArea() {
         return (width * height); 
      }
}
 
class Square: Rectangle {
   this(int side) {
      this.setWidth(side); 
      this.setHeight(side); 
   }
}

void main() {
   Square square = new Square(13);

   // Вывод площади объекта. 
   writeln("Общая площадь: ", square.getArea());
}

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

Общая площадь: 169

Предыдущая страница
Следующая страница