Наследование¶
Ü поддерживает наследование для классов. Наследование позволяет создавать классы, частично заимствующие состав и поведение классов-предков. Наследование также позволяет реализовать динамический полиморфизм.
В наследовании могут принимать участие только полиморфные классы. Полиморфными являются классы, объявленные с использованием ключевых слов polymorph
, abstract
или inteface
, или классы, имеющие предков.
class A polymorph // Класс, от которого можно наследоваться.
{}
class B interface // Интерфейс. От него можно наследоваться, но сам он имеет ряд ограничений на своё содержимое.
{}
class C abstract // Тоже полиморфный класс, но в котором разрешены нереализованные виртуальные функции.
{}
class D : C // Полиморфный класс, т. к. имеет предка.
{}
class E : B // Полиморфный класс с предком-интерфейсом.
{}
class F : A, B // Полиморфный класс с двумя предками, один из которых - интерфейс.
{}
class G final : F // Полиморфный класс, от которого нельзя унаследоваться.
{}
Правила наследования¶
Класс может иметь не более одного неинтерфейсного предка и любое количество интерфейсных предков.
Неинтерфейсный предковый класс считается базой класса. Обратиться к нему можно с использованием ключевого слова base
.
base
- это this
, но преобразованный в тип базового класса.
Существует ряд особых видов полиморфных классов:
interface
класс, это такой полиморфный класс, у которого не может быть базового класса, полей, виртуальных методов с реализацией.final
класс, это такой полиморфный класс, от которого уже нельзя унаследоваться.abstract
класс, это такой полиморфный класс, у которого могут быть нереализованные виртуальные методы. У полиморфных классов, не помеченных какabstract
илиinterface
все виртуальные методы должны иметь реализацию.
Ограничения абстрактных классов и интерфейсов¶
Абстрактные классы и интерфейсы не являются полноценными классами, они имеют ряд ограничений:
- Нельзя создать экземпляр абстрактного класса или интерфейса, вместо этого можно только унаследоваться от них и создать экземпляр класса-потомка.
- Интерфейсы не могут иметь конструкторов. Они им не нужны, т. к. у них нету полей, которые необходимо было бы инициализировать в конструкторах.
- Абстрактные классы могут иметь конструкторы, но в них не доступен
this
, что означает, что можно обращаться только к конкретным полям по имени и нельзя вызывать никакие методы. - Интерфейсы и абстрактные классы могут иметь деструкторы, но в них
this
также не доступен.
Преобразование ссылок¶
Ссылка на класс-потомок может быть явно или неявно преобразована в ссылку на класс-предок. Неявные преобразования возможны:
- При инициализации ссылочной переменной или ссылочного поля
- При передаче аргумента в функцию
- При возврате значения из функции
Состав наследуемого содержимого¶
Класс-потомок наследует от класса-предка public
и protected
члены. private
члены класса не наследуются.
Не наследуются конструкторы, деструкторы, операторы присваивания, операторы сравнения (==
, <=>
).
Методы наследуются, но тип this
у них остаётся таким же, как и у предкового класса, за исключением случая, когда происходит переопределение виртуального метода.
Виртуальные методы¶
Виртуальный метод - это такой метод, который вызывается косвенно, и конкретная реализация которого может быть различной. Виртуальный метод объявленный в одном классе, может быть переопределён в классе-потомке.
Объявляется виртуальный метод с использованием ряда ключевых слов:
virtual
- метод впервые объявляется как виртуальный. В этом случае у предкового класса не должно быть такого метода.virtual pure
- метод впервые объявляется как виртуальный. Реализация данного метода запрещена.virtual override
- метод переопределяет метод предкового класса.virtual final
- метод переопределяет метод предкового класса. Дальнейшей переопределение в классах-потомках для этого метода запрещено.
Виртуальные методы должны всегда иметь параметр this
.
Он должен быть изменяемой или неизменяемой ссылкой, byval
this
не допускается.
class A interface
{
fn virtual pure Foo( this, i32 x );
}
class B polymorph
{
fn virtual Bar( mut this, f32 y );
}
class C : A, B
{
fn virtual override Foo( this, i32 x );
fn virtual final Bar( mut this, f32 y );
}
fn CallFoo( A& a, i32 x )
{
a.Foo(x);
}
fn CallBar( B &mut b, f32 y )
{
b.Bar(y);
}
fn Test()
{
var C mut c;
CallFoo( c, 42 ); // Будет вызван метод С::Foo
CallBar( c, 0.25f ); // Будет вызван метод C::Bar
var B mut b;
CallBar( b, 3.14f ); // Будет вызван метод B::Bar
}
Деструктор полиморфных классов всегда является виртуальным, его можно таковым и не объявлять.