Наследование (программирование)

Поделись знанием:
Перейти к: навигация, поиск

Наследование (англ. inheritance) — концепция объектно-ориентированного программирования, процесс приведения к одному значению одного или нескольких свойств разных объектов





Терминология

В объектно-ориентированном программировании, начиная с «Simula 67», абстрактные типы данных называются классами.

Класс, опредёленный через наследование от другого класса, называется: производным классом, классом потомком (англ. derived class) или подклассом (англ. subclass). Класс, от которого новый класс наследуется, называется: предком (англ. parent), базовым классом (англ. base class) или суперклассом (англ. parent class).

Применение

Наследование является механизмом повторного использования кода (англ. code reuse) и способствует независимому расширению программного обеспечения через открытые классы (англ. public classes) и интерфейсы (англ. interfaces). Установка отношения наследования между классами порождает иерархию классов (англ. class hierarchy).

Наследование и полиморфизм подтипов

Часто наследование отождествляют с полиморфизмом подтипов (англ. subtyping):

Несмотря на приведённое выше замечание, наследование является широко используемым механизмом установки отношения «является» (англ. is-a relationship). Некоторые языки программирования согласуют наследование и полиморфизм подтипов (в основном, это относится к языкам со статической типизацией: «C++»«C#»«Java» и «Scala») — в то время как другие разделяют вышеописанные концепции.

Наследование, — даже в языках программирования, которые поддерживают применение наследования как механизма, обеспечивающего полиморфизм подтипов, — не гарантирует поведенческий полиморфизм подтипов; смотри: «Принцип подстановки» Барбары Лисков.

Типы наследования

«Простое» наследование

Терминология

  • «Базовые», «родительские» (англ. base class) — классы, производящие наследование;
  • «Потомки», «наследники», «производные» (англ. derived class) — классы, наследуемые от базовых.

Абстрактные классы и создание объектов

Для некоторых языков программирования справедлива следующая концепция.

Существуют «абстрактные» классы (объявляемые таковыми произвольно или из-за приписанных им абстрактных методов); их можно описывать имеющими поля и методы. Создание же объектов (экземпляров) означает конкретизацию, применимую лишь к неабстрактным классам (в том числе, к неабстрактным наследникам абстрактных), — представителями которых, по итогу, будут созданные объекты.

Пример: Пусть базовый класс «Сотрудник ВУЗа», — от которого наследуются классы «Аспирант» и «Профессор», — абстрактный. Общие поля и функции классов (например, поле «Год рождения») могут быть описаны в базовом классе. И в программе будут созданы объекты только производных классов: «Аспирант» и «Профессор»; обычно нет смысла создавать объекты базовых классов.

Множественное наследование

При множественном наследовании, у класса может быть более одного предка. В этом случае класс наследует методы всех предков. Достоинства такого подхода в большей гибкости.

Множественное наследование реализовано в «C++». Из других языков, предоставляющих эту возможность, можно отметить «Python» и «Eiffel». Множественное наследование поддерживается в языке «UML».

Множественное наследование — потенциальный источник ошибок, которые могут возникнуть из-за наличия одинаковых имён методов в предках. В языках, которые позиционируются как наследники «C++» («Java», «C#» и другие), от множественного наследования было решено отказаться в пользу интерфейсов. Практически всегда можно обойтись без использования данного механизма. Однако, если такая необходимость всё-таки возникла, то для разрешения конфликтов использования наследованных методов с одинаковыми именами возможно, например, применить операцию расширения видимости — «::» — для вызова конкретного метода конкретного родителя.

Попытка решения проблемы наличия одинаковых имён методов в предках была предпринята в языке «Eiffel», в котором при описании нового класса необходимо явно указывать импортируемые члены каждого из наследуемых классов и их именование в дочернем классе.

Большинство современных объектно-ориентированных языков программирования («C#», «Java», «Delphi» и другие) поддерживают возможность одновременно наследоваться от класса-предка и реализовать методы нескольких интерфейсов одним и тем же классом. Этот механизм позволяет во многом заменить множественное наследование — методы интерфейсов необходимо переопределять явно, что исключает ошибки при наследовании функциональности одинаковых методов различных классов-предков.

Единый базовый класс

В ряде языков программирования, все классы, — явно или неявно, — наследуются от некого базового класса. «Smalltalk» был одним из первых языков, в которых использовалась эта концепция. К таким языкам также относятся: «Objective-C» (класс NSObject), «Perl» (UNIVERSAL), «Eiffel» (ANY), «Java» (java.lang.Object), «C#» (System.Object), «Delphi» (TObject), «Scala» (Any).

Наследование в языках программирования

«C++»

Наследование в «C++»:

class A {}; // Базовый класс

class B : public A {}; // Public-наследование
class C : protected A {}; // Protected-наследование
class Z : private A {}; // Private-наследование

В «C++» существует три типа наследования: public, protected, private. Спецификаторы доступа членов базового класса меняются в потомках следующим образом:

  • Если класс объявлен как базовый для другого класса со спецификатором доступа …
    • … «public»:
      • «public»-члены базового класса — доступны как «public»-члены производного класса;
      • «protected»-члены базового класса — доступны как «protected»-члены производного класса;
    • … «protected»:
      • «public»- и «protected»- члены базового класса — доступны как «protected»-члены производного класса;
    • … «private»:
      • «public»- и «protected»- члены базового класса — доступны как «private»-члены производного класса.

Одним из основных преимуществ «public»-наследования является то, что указатель на классы-наследники может быть неявно преобразован в указатель на базовый класс, то есть для примера выше можно написать:

A* a = new B();

Эта интересная особенность открывает возможность динамической идентификации типа (RTTI).

«Delphi» («Object Pascal»)

Для использования механизма наследования в «Delphi» необходимо в объявлении класса справа от слова class указать класс предок:

Предок:

TAncestor = class
private
protected
public
  // Виртуальная процедура
  procedure VirtualProcedure; virtual; abstract; 
  procedure StaticProcedure;
end;

Наследник:

TDescendant = class(TAncestor)
private
protected
public
  // Перекрытие виртуальной процедуры
  procedure VirtualProcedure; override;
  procedure StaticProcedure;
end;

Абсолютно все классы в «Delphi» являются потомками класса TObject. Если класс-предок не указан, то подразумевается, что новый класс является прямым потомком класса TObject.

Множественное наследование в «Delphi» частично поддерживается за счёт использования классов-помощников (Сlass Helpers).

«Python»

«Python» поддерживает как одиночное, так и множественное наследование. При доступе к атрибуту, просмотр производных классов происходит в порядке разрешения метода (англ. method resolution order, MRO)[1].

class Ancestor1(object):   # Предок-1
    def m1(self): pass
class Ancestor2(object):   # Предок-2
    def m1(self): pass
class Descendant(Ancestor1, Ancestor2):   # Наследник
    def m2(self): pass

d = Descendant()           # Инстанциация
print d.__class__.__mro__  # Порядок разрешения метода:
(<class '__main__.Descendant'>, <class '__main__.Ancestor1'>, <class '__main__.Ancestor2'>, <type 'object'>)

С версии «Python» 2.2 в языке сосуществуют «классические» классы и «новые» классы. Последние являются наследниками object. «Классические» классы будут поддерживаться вплоть до версии 2.6, но удалены из языка в «Python» версии 3.0.

Множественное наследование применяется в «Python», в частности, для введения в основной класс классов-примесей (англ. mix-in).

«PHP»

Для использования механизма наследования в «PHP» необходимо в объявлении класса после имени объявляемого класса-наследника указать слово extends и имя класса-предка:

class Descendant extends Ancestor {
}

В случае перекрытия классом-наследником методов предка, доступ к методам предка можно получить с использованием ключевого слова parent:

class A {
  function example() {
    echo "Вызван метод A::example().<br />\n";
  }
}

class B extends A {
  function example() {
    echo "Вызван метод B::example().<br />\n";
    parent::example();
  }
}

Можно предотвратить перекрытие классом-наследником методов предка; для этого необходимо указать ключевое слово final:

class A {
  final function example() {
    echo "Вызван метод A::example().<br />\n";
  }
}

class B extends A {
  function example() { //вызовет ошибку
    parent::example(); //и никогда не выполнится
  }
}
Чтобы при наследовании обратиться к конструктору родительского класса, необходимо дочернему классу в конструкторе указать parent::__construct();[2]

«Objective-C»

@interface A : NSObject 
- (void) example;
@end

@implementation
- (void) example
{
    NSLog(@"Class A");
}
@end

@interface B : A
- (void) example;
@end

@implementation
- (void) example
{
    NSLog(@"Class B");
}
@end

В интерфейсе объявляют методы, которые будут видны снаружи класса (public).

Внутренние методы можно реализовывать без интерфейса. Для объявления дополнительных свойств, пользуются interface-extension в файле реализации.

Все методы в «Objective-C» виртуальные.

«Java»

Пример наследования от одного класса и двух интерфейсов:

        public class A { }
        public interface I1 { }
        public interface I2 { }
        public class B extends A implements I1, I2 { }

Директива final в объявлении класса делает наследование от него невозможным.

«C#»

Пример наследования от одного класса и двух интерфейсов:

        public class A { }
        public interface I1 { }
        public interface I2 { }
        public class B : A, I1, I2 { }

Наследование от типизированных классов можно осуществлять указав фиксированный тип, либо путём переноса переменной типа в наследуемый класс:

        public class A<T>
        { }
        public class B : A<int>
        { }
        public class B2<T> : A<T>
        { }

Допустимо также наследование вложенных классов от классов, их содержащих:

    class A // default class A is internal, not public class B can not be public

    {
        class B : A { }
    }

Директива sealed в объявлении класса делает наследование от него невозможным.[3]

«Ruby»

class Parent

  def public_method
    "Public method"
  end

  private

    def private_method
      "Private method"
    end

end

class Child < Parent

  def public_method
    "Redefined public method"
  end

  def call_private_method
    "Ancestor's private method: " + private_method
  end

end

Класс Parent является предком для класса Child, у которого переопределён метод public_method.

child = Child.new
child.public_method #=> "Redefined public method"
child.call_private_method #=> "Ancestor's private method: Private method"

Приватные методы предка можно вызывать из наследников.

«JavaScript»

var Parent = function( data ) {
    this.data = data || false;
    this.public_method = function() { return 'Public Method'; }
}

var Child = function() {
    this.public_method = function() { return 'Redefined public method'; }
    this.getData = function() { return 'Data: ' + this.data; }
}

Child.prototype = new Parent('test');
var Test = new Child();

Test.getData(); // => "Data: test"
Test.public_method(); // => "Redefined public method"
Test.data; // => "test"

Класс Parent является предком для класса Child, у которого переопределен метод public_method.

В «JavaScript» используется прототипное наследование.

Конструкторы и деструкторы

В «С++» конструкторы при наследовании вызываются последовательно от самого раннего предка до самого позднего потомка, а деструкторы наоборот — от самого позднего потомка до самого раннего предка.

class First
{
public:
    First()  { cout << ">>First constructor" << endl; }
    ~First() { cout << ">>First destructor" << endl; }
};

class Second: public First
{
public:
    Second()  { cout << ">Second constructor" << endl; }
    ~Second() { cout << ">Second destructor" << endl; }
};

class Third: public Second
{
public:
    Third()  { cout << "Third constructor" << endl; }
    ~Third() { cout << "Third destructor" << endl; }
};

// выполнение кода
Third *th = new Third();
delete th;

// результат вывода
/*
>>First constructor
>Second constructor
Third constructor

Third destructor
>Second destructor
>>First destructor
*/

Напишите отзыв о статье "Наследование (программирование)"

Ссылки

  • [cplus.about.com/od/beginnerctutorial/l/aa121302a.htm Multiple Inheritance]
  • [www.quizful.net/post/multiple-typecasting-problems Проблемы множественного динамического приведения типов и их решения]

Примечания

  1. [www.python.org/download/releases/2.3/mro/ о порядке разрешения метода в Python]
  2. [ru.wh-db.com/article/chto-takoe-objecno-orientirovannoe-programmirovanie/ Что такое объектно-ориентированное программирование]. wh-db.com (30.06.2015).
  3. C# Language Specification Version 4.0, Copyright © Microsoft Corporation 1999—2010


Отрывок, характеризующий Наследование (программирование)

Когда государь объехал почти все полки, войска стали проходить мимо его церемониальным маршем, и Ростов на вновь купленном у Денисова Бедуине проехал в замке своего эскадрона, т. е. один и совершенно на виду перед государем.
Не доезжая государя, Ростов, отличный ездок, два раза всадил шпоры своему Бедуину и довел его счастливо до того бешеного аллюра рыси, которою хаживал разгоряченный Бедуин. Подогнув пенящуюся морду к груди, отделив хвост и как будто летя на воздухе и не касаясь до земли, грациозно и высоко вскидывая и переменяя ноги, Бедуин, тоже чувствовавший на себе взгляд государя, прошел превосходно.
Сам Ростов, завалив назад ноги и подобрав живот и чувствуя себя одним куском с лошадью, с нахмуренным, но блаженным лицом, чортом , как говорил Денисов, проехал мимо государя.
– Молодцы павлоградцы! – проговорил государь.
«Боже мой! Как бы я счастлив был, если бы он велел мне сейчас броситься в огонь», подумал Ростов.
Когда смотр кончился, офицеры, вновь пришедшие и Кутузовские, стали сходиться группами и начали разговоры о наградах, об австрийцах и их мундирах, об их фронте, о Бонапарте и о том, как ему плохо придется теперь, особенно когда подойдет еще корпус Эссена, и Пруссия примет нашу сторону.
Но более всего во всех кружках говорили о государе Александре, передавали каждое его слово, движение и восторгались им.
Все только одного желали: под предводительством государя скорее итти против неприятеля. Под командою самого государя нельзя было не победить кого бы то ни было, так думали после смотра Ростов и большинство офицеров.
Все после смотра были уверены в победе больше, чем бы могли быть после двух выигранных сражений.


На другой день после смотра Борис, одевшись в лучший мундир и напутствуемый пожеланиями успеха от своего товарища Берга, поехал в Ольмюц к Болконскому, желая воспользоваться его лаской и устроить себе наилучшее положение, в особенности положение адъютанта при важном лице, казавшееся ему особенно заманчивым в армии. «Хорошо Ростову, которому отец присылает по 10 ти тысяч, рассуждать о том, как он никому не хочет кланяться и ни к кому не пойдет в лакеи; но мне, ничего не имеющему, кроме своей головы, надо сделать свою карьеру и не упускать случаев, а пользоваться ими».
В Ольмюце он не застал в этот день князя Андрея. Но вид Ольмюца, где стояла главная квартира, дипломатический корпус и жили оба императора с своими свитами – придворных, приближенных, только больше усилил его желание принадлежать к этому верховному миру.
Он никого не знал, и, несмотря на его щегольской гвардейский мундир, все эти высшие люди, сновавшие по улицам, в щегольских экипажах, плюмажах, лентах и орденах, придворные и военные, казалось, стояли так неизмеримо выше его, гвардейского офицерика, что не только не хотели, но и не могли признать его существование. В помещении главнокомандующего Кутузова, где он спросил Болконского, все эти адъютанты и даже денщики смотрели на него так, как будто желали внушить ему, что таких, как он, офицеров очень много сюда шляется и что они все уже очень надоели. Несмотря на это, или скорее вследствие этого, на другой день, 15 числа, он после обеда опять поехал в Ольмюц и, войдя в дом, занимаемый Кутузовым, спросил Болконского. Князь Андрей был дома, и Бориса провели в большую залу, в которой, вероятно, прежде танцовали, а теперь стояли пять кроватей, разнородная мебель: стол, стулья и клавикорды. Один адъютант, ближе к двери, в персидском халате, сидел за столом и писал. Другой, красный, толстый Несвицкий, лежал на постели, подложив руки под голову, и смеялся с присевшим к нему офицером. Третий играл на клавикордах венский вальс, четвертый лежал на этих клавикордах и подпевал ему. Болконского не было. Никто из этих господ, заметив Бориса, не изменил своего положения. Тот, который писал, и к которому обратился Борис, досадливо обернулся и сказал ему, что Болконский дежурный, и чтобы он шел налево в дверь, в приемную, коли ему нужно видеть его. Борис поблагодарил и пошел в приемную. В приемной было человек десять офицеров и генералов.
В то время, как взошел Борис, князь Андрей, презрительно прищурившись (с тем особенным видом учтивой усталости, которая ясно говорит, что, коли бы не моя обязанность, я бы минуты с вами не стал разговаривать), выслушивал старого русского генерала в орденах, который почти на цыпочках, на вытяжке, с солдатским подобострастным выражением багрового лица что то докладывал князю Андрею.
– Очень хорошо, извольте подождать, – сказал он генералу тем французским выговором по русски, которым он говорил, когда хотел говорить презрительно, и, заметив Бориса, не обращаясь более к генералу (который с мольбою бегал за ним, прося еще что то выслушать), князь Андрей с веселой улыбкой, кивая ему, обратился к Борису.
Борис в эту минуту уже ясно понял то, что он предвидел прежде, именно то, что в армии, кроме той субординации и дисциплины, которая была написана в уставе, и которую знали в полку, и он знал, была другая, более существенная субординация, та, которая заставляла этого затянутого с багровым лицом генерала почтительно дожидаться, в то время как капитан князь Андрей для своего удовольствия находил более удобным разговаривать с прапорщиком Друбецким. Больше чем когда нибудь Борис решился служить впредь не по той писанной в уставе, а по этой неписанной субординации. Он теперь чувствовал, что только вследствие того, что он был рекомендован князю Андрею, он уже стал сразу выше генерала, который в других случаях, во фронте, мог уничтожить его, гвардейского прапорщика. Князь Андрей подошел к нему и взял за руку.
– Очень жаль, что вчера вы не застали меня. Я целый день провозился с немцами. Ездили с Вейротером поверять диспозицию. Как немцы возьмутся за аккуратность – конца нет!
Борис улыбнулся, как будто он понимал то, о чем, как об общеизвестном, намекал князь Андрей. Но он в первый раз слышал и фамилию Вейротера и даже слово диспозиция.
– Ну что, мой милый, всё в адъютанты хотите? Я об вас подумал за это время.
– Да, я думал, – невольно отчего то краснея, сказал Борис, – просить главнокомандующего; к нему было письмо обо мне от князя Курагина; я хотел просить только потому, – прибавил он, как бы извиняясь, что, боюсь, гвардия не будет в деле.
– Хорошо! хорошо! мы обо всем переговорим, – сказал князь Андрей, – только дайте доложить про этого господина, и я принадлежу вам.
В то время как князь Андрей ходил докладывать про багрового генерала, генерал этот, видимо, не разделявший понятий Бориса о выгодах неписанной субординации, так уперся глазами в дерзкого прапорщика, помешавшего ему договорить с адъютантом, что Борису стало неловко. Он отвернулся и с нетерпением ожидал, когда возвратится князь Андрей из кабинета главнокомандующего.
– Вот что, мой милый, я думал о вас, – сказал князь Андрей, когда они прошли в большую залу с клавикордами. – К главнокомандующему вам ходить нечего, – говорил князь Андрей, – он наговорит вам кучу любезностей, скажет, чтобы приходили к нему обедать («это было бы еще не так плохо для службы по той субординации», подумал Борис), но из этого дальше ничего не выйдет; нас, адъютантов и ординарцев, скоро будет батальон. Но вот что мы сделаем: у меня есть хороший приятель, генерал адъютант и прекрасный человек, князь Долгоруков; и хотя вы этого можете не знать, но дело в том, что теперь Кутузов с его штабом и мы все ровно ничего не значим: всё теперь сосредоточивается у государя; так вот мы пойдемте ка к Долгорукову, мне и надо сходить к нему, я уж ему говорил про вас; так мы и посмотрим; не найдет ли он возможным пристроить вас при себе, или где нибудь там, поближе .к солнцу.
Князь Андрей всегда особенно оживлялся, когда ему приходилось руководить молодого человека и помогать ему в светском успехе. Под предлогом этой помощи другому, которую он по гордости никогда не принял бы для себя, он находился вблизи той среды, которая давала успех и которая притягивала его к себе. Он весьма охотно взялся за Бориса и пошел с ним к князю Долгорукову.
Было уже поздно вечером, когда они взошли в Ольмюцкий дворец, занимаемый императорами и их приближенными.
В этот самый день был военный совет, на котором участвовали все члены гофкригсрата и оба императора. На совете, в противность мнения стариков – Кутузова и князя Шварцернберга, было решено немедленно наступать и дать генеральное сражение Бонапарту. Военный совет только что кончился, когда князь Андрей, сопутствуемый Борисом, пришел во дворец отыскивать князя Долгорукова. Еще все лица главной квартиры находились под обаянием сегодняшнего, победоносного для партии молодых, военного совета. Голоса медлителей, советовавших ожидать еще чего то не наступая, так единодушно были заглушены и доводы их опровергнуты несомненными доказательствами выгод наступления, что то, о чем толковалось в совете, будущее сражение и, без сомнения, победа, казались уже не будущим, а прошедшим. Все выгоды были на нашей стороне. Огромные силы, без сомнения, превосходившие силы Наполеона, были стянуты в одно место; войска были одушевлены присутствием императоров и рвались в дело; стратегический пункт, на котором приходилось действовать, был до малейших подробностей известен австрийскому генералу Вейротеру, руководившему войска (как бы счастливая случайность сделала то, что австрийские войска в прошлом году были на маневрах именно на тех полях, на которых теперь предстояло сразиться с французом); до малейших подробностей была известна и передана на картах предлежащая местность, и Бонапарте, видимо, ослабленный, ничего не предпринимал.
Долгоруков, один из самых горячих сторонников наступления, только что вернулся из совета, усталый, измученный, но оживленный и гордый одержанной победой. Князь Андрей представил покровительствуемого им офицера, но князь Долгоруков, учтиво и крепко пожав ему руку, ничего не сказал Борису и, очевидно не в силах удержаться от высказывания тех мыслей, которые сильнее всего занимали его в эту минуту, по французски обратился к князю Андрею.
– Ну, мой милый, какое мы выдержали сражение! Дай Бог только, чтобы то, которое будет следствием его, было бы столь же победоносно. Однако, мой милый, – говорил он отрывочно и оживленно, – я должен признать свою вину перед австрийцами и в особенности перед Вейротером. Что за точность, что за подробность, что за знание местности, что за предвидение всех возможностей, всех условий, всех малейших подробностей! Нет, мой милый, выгодней тех условий, в которых мы находимся, нельзя ничего нарочно выдумать. Соединение австрийской отчетливости с русской храбростию – чего ж вы хотите еще?
– Так наступление окончательно решено? – сказал Болконский.
– И знаете ли, мой милый, мне кажется, что решительно Буонапарте потерял свою латынь. Вы знаете, что нынче получено от него письмо к императору. – Долгоруков улыбнулся значительно.
– Вот как! Что ж он пишет? – спросил Болконский.
– Что он может писать? Традиридира и т. п., всё только с целью выиграть время. Я вам говорю, что он у нас в руках; это верно! Но что забавнее всего, – сказал он, вдруг добродушно засмеявшись, – это то, что никак не могли придумать, как ему адресовать ответ? Ежели не консулу, само собою разумеется не императору, то генералу Буонапарту, как мне казалось.
– Но между тем, чтобы не признавать императором, и тем, чтобы называть генералом Буонапарте, есть разница, – сказал Болконский.
– В том то и дело, – смеясь и перебивая, быстро говорил Долгоруков. – Вы знаете Билибина, он очень умный человек, он предлагал адресовать: «узурпатору и врагу человеческого рода».
Долгоруков весело захохотал.
– Не более того? – заметил Болконский.
– Но всё таки Билибин нашел серьезный титул адреса. И остроумный и умный человек.
– Как же?
– Главе французского правительства, au chef du gouverienement francais, – серьезно и с удовольствием сказал князь Долгоруков. – Не правда ли, что хорошо?
– Хорошо, но очень не понравится ему, – заметил Болконский.
– О, и очень! Мой брат знает его: он не раз обедал у него, у теперешнего императора, в Париже и говорил мне, что он не видал более утонченного и хитрого дипломата: знаете, соединение французской ловкости и итальянского актерства? Вы знаете его анекдоты с графом Марковым? Только один граф Марков умел с ним обращаться. Вы знаете историю платка? Это прелесть!
И словоохотливый Долгоруков, обращаясь то к Борису, то к князю Андрею, рассказал, как Бонапарт, желая испытать Маркова, нашего посланника, нарочно уронил перед ним платок и остановился, глядя на него, ожидая, вероятно, услуги от Маркова и как, Марков тотчас же уронил рядом свой платок и поднял свой, не поднимая платка Бонапарта.