sources
This commit is contained in:
parent
1d0a2964cf
commit
151d77ee54
|
@ -105,6 +105,8 @@ unittest
|
|||
|
||||
Обратите внимание на небольшую хитрость. В приведенном коде использовано выражение `w.defaultName`, а не `Widget.defaultName`. Для обращения к статическому члену класса всегда можно вместо имени класса использовать имя экземпляра класса. Это возможно, потому что при обработке выражения слева от точки сначала выполняется разрешение имени и только потом идентификация объекта (если потребуется). Выражение w в любом случае вычисляется: будет оно использовано или нет.
|
||||
|
||||
[Исходный код](src/chapter-6-1/)
|
||||
|
||||
[В начало ⮍](#6-1-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
## 6.2. Имена объектов – это ссылки
|
||||
|
@ -214,6 +216,8 @@ if (‹условие›)
|
|||
|
||||
Сравним ссылочную семантику с семантикой значений а-ля `int`. У семантики значений есть свои преимущества, среди которых выделяется логический вывод: в выражениях всегда можно заменять равные значения друг на друга, при этом результат не изменяется. (А к ссылкам, использующим для изменения состояния объектов вызовы методов, такой подход неприменим.) Другое важное преимущество семантики значений – скорость. Но даже если вы воспользуетесь динамической щедростью полиморфизма, от ссылочной семантики никуда не деться. Некоторые языки пытались предоставить возможность использовать и ту, и другую семантику и заслужили прозвище «нечистых» (в противоположность чисто объектно-ориентированным языкам, использующим ссылочную семантику унифицированно для всех типов). D нечист и очень гордится этим. Во время разработки необходимо принять решение: если вы желаете работать с некоторым типом в рамках объектно-ориентированной парадигмы, следует выбрать тип `class`; иначе придется использовать тип `struct` и поступиться всеми удобствами ООП, присущими ссылочной семантике.
|
||||
|
||||
[Исходный код](src/chapter-6-2/)
|
||||
|
||||
[В начало ⮍](#6-2-имена-объектов-это-ссылки) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
## 6.3. Жизненный цикл объекта
|
||||
|
@ -239,6 +243,8 @@ unittest
|
|||
|
||||
При вычислении выражения `new Test` конструируется объект типа `Test` с состоянием по умолчанию, то есть экземпляр класса `Test`, каждое из полей которого инициализировано своим значением по умолчанию. Любой тип `T` обладает статически известным значением по умолчанию, обратиться к которому можно через свойство `T.init` (значения свойств `.init` для базовых типов приведены в табл. 2.1). Если вы хотите инициализировать некоторые поля значениями, отличными от соответствующих значений свойства `.init`, укажите при определении этих полей статически известные инициализирующие значения, как показано в предыдущем примере для поля `a`. Выполнение теста модуля при этом не порождает исключений, так как это поле явно инициализируется константой `0.4`, а поле `b` не трогали, а значит, оно неявно инициализируется значением выражения `double.init`, то есть NaN («нечисло»).
|
||||
|
||||
[Исходный код](src/chapter-6-3/)
|
||||
|
||||
[В начало ⮍](#6-3-жизненный-цикл-объекта) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
### 6.3.1. Конструкторы
|
||||
|
@ -303,6 +309,8 @@ class NoGo
|
|||
|
||||
Обычные правила перегрузки функций (раздел 5.5) применимы и к конструкторам: класс может определять любое количество конструкторов, но каждый из них должен обладать уникальной сигнатурой (отличающейся числом или типом параметров, хотя бы на один параметр).
|
||||
|
||||
[Исходный код](src/chapter-6-3-1/)
|
||||
|
||||
[В начало ⮍](#6-3-1-конструкторы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
### 6.3.2. Делегирование конструкторов
|
||||
|
@ -722,6 +730,8 @@ unittest
|
|||
|
||||
Если бы занявший место экземпляра класса `Contact` экземпляр класса `Friend` вел себя *в точности* так же, как и экземпляр ожидаемого класса, отпали бы все (или почти все) причины использовать класс `Friend`. Одно из основных средств, предоставляемых объектной технологией, – возможность классам-наследникам переопределять функции классов-предков и таким образом модульно настраивать поведение сущностей среды. Как можно догадаться, переопределение задается с помощью ключевого слова `override` (класс `Friend` переопределяет метод `bgColor`), которое обозначает, что вызов `c.bgColor()` (где вместо c ожидается объект типа `Contact`, но на самом деле используется объект типа `Friend`) всегда инициирует вызов версии метода, предлагаемой классом `Friend`. Друзья всегда остаются друзьями, даже если компилятор думает, что это обыкновенные контакты.
|
||||
|
||||
[Исходный код](src/chapter-6-4/)
|
||||
|
||||
[В начало ⮍](#6-4-методы-и-наследование) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
### 6.4.1. Терминологический «шведский стол»
|
||||
|
@ -902,6 +912,8 @@ void workWith(TextWidget tw)
|
|||
|
||||
Чтобы максимизировать количество доступной статической информации о типах, D вводит средство, известное как *ковариантные возвращаемые типы*. Звучит довольно громко, но смысл ковариантности возвращаемых типов довольно прост: если родительский класс возвращает некоторый тип `C`, то переопределенной функции разрешается возвращать не только `C`, но и любого потомка `C`. Благодаря этому средству можно позволить методу `TextWidget.duplicate` возвращать `TextWidget`. Не менее важно, что теперь вы можете прибавить себе веса в дискуссии, вставив при случае фразу «ковариантные возвращаемые типы». (Шутка. Если серьезно, даже не пытайтесь.)
|
||||
|
||||
[Исходный код](src/chapter-6-4-5/)
|
||||
|
||||
[В начало ⮍](#6-4-5-ковариантные-возвращаемые-типы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
## 6.5. Инкапсуляция на уровне классов с помощью статических членов
|
||||
|
@ -1140,6 +1152,8 @@ unittest
|
|||
|
||||
Обратите внимание: вместе с именем класса возвращено и имя модуля, в котором класс был определен. По умолчанию модуль получает имя файла, в котором он расположен, но это умолчание можно изменить с помощью объявления с ключевым словом `module` (см. раздел 11.8).
|
||||
|
||||
[Исходный код](src/chapter-6-8-1/)
|
||||
|
||||
[В начало ⮍](#6-8-1-string-tostring) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
### 6.8.2. size_t toHash()
|
||||
|
@ -1882,6 +1896,8 @@ class Outer
|
|||
}
|
||||
```
|
||||
|
||||
[Исходный код](src/chapter-6-11/)
|
||||
|
||||
[В начало ⮍](#6-11-вложенные-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
### 6.11.1. Вложенные классы в функциях
|
||||
|
@ -2244,6 +2260,8 @@ class StorableShape : Shape
|
|||
}
|
||||
```
|
||||
|
||||
[Исходный код](src/chapter-6-13-1/)
|
||||
|
||||
[В начало ⮍](#6-13-1-переопределение-методов-в-сценариях-множественного-порождения-подтипов) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
## 6.14. Параметризированные классы и интерфейсы
|
||||
|
@ -2325,6 +2343,8 @@ unittest
|
|||
|
||||
Как только вы создадите экземпляр параметризированного класса, он превратится в обычный класс, так что `StackImpl!int` – это такой же класс, как и любой другой. Именно этот конкретный класс реализует `Stack!int`, поскольку в формочку для вырезания `StackImpl(T)` под видом `T` вставили `int` по всему телу этого класса.
|
||||
|
||||
[Исходный код](src/chapter-6-14/)
|
||||
|
||||
[В начало ⮍](#6-14-параметризированные-классы-и-интерфейсы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
|
||||
|
||||
### 6.14.1. И снова гетерогенная трансляция
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
class Widget
|
||||
{
|
||||
// Константа
|
||||
enum fudgeFactor = 0.2;
|
||||
|
||||
// Разделяемое неизменяемое значение
|
||||
static immutable defaultName = "A Widget";
|
||||
|
||||
// Некоторое состояние, определенное для всех экземпляров класса Widget
|
||||
string name = defaultName;
|
||||
uint width, height;
|
||||
|
||||
// Статический метод
|
||||
static double howFudgy()
|
||||
{
|
||||
return fudgeFactor;
|
||||
}
|
||||
|
||||
// Метод
|
||||
void changeName(string another)
|
||||
{
|
||||
name = another;
|
||||
}
|
||||
|
||||
// Метод, который нельзя переопределить
|
||||
final void quadrupleSize()
|
||||
{
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
// Обратиться к статическому методу класса Widget
|
||||
assert(Widget.howFudgy() == 0.2);
|
||||
// Создать экземпляр класса Widget
|
||||
auto w = new Widget;
|
||||
// Поиграть с объектом типа Widget
|
||||
assert(w.name == w.defaultName); // Или Widget.defaultName
|
||||
w.changeName("Мой виджет");
|
||||
assert(w.name == "Мой виджет");
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
class Outer
|
||||
{
|
||||
int x;
|
||||
|
||||
class Inner
|
||||
{
|
||||
int y;
|
||||
|
||||
this()
|
||||
{
|
||||
x = 42;
|
||||
// x – то же, что this.outer.x
|
||||
assert(this.outer.x == 42);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto outer = new Outer;
|
||||
auto inner = outer.new Inner;
|
||||
assert(outer.x == 42); // Вложенный объект inner изменил внешний объект outer
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
import std.stdio : writeln;
|
||||
|
||||
class Shape
|
||||
{
|
||||
protected string _name;
|
||||
abstract void print();
|
||||
}
|
||||
|
||||
class DBObject
|
||||
{
|
||||
protected string _name;
|
||||
abstract void print();
|
||||
void saveState()
|
||||
{
|
||||
writeln(_name);
|
||||
}
|
||||
}
|
||||
|
||||
class StorableShape : Shape
|
||||
{
|
||||
private class MyDBObject : DBObject
|
||||
{
|
||||
this(string name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
override void print()
|
||||
{
|
||||
writeln(_name);
|
||||
}
|
||||
|
||||
final override void saveState()
|
||||
{
|
||||
writeln(this.outer._name, "; ", _name);
|
||||
}
|
||||
}
|
||||
|
||||
private MyDBObject _store;
|
||||
alias _store this;
|
||||
|
||||
this(string outName, string inName)
|
||||
{
|
||||
_name = outName;
|
||||
_store = new MyDBObject(inName);
|
||||
}
|
||||
|
||||
override void print()
|
||||
{
|
||||
writeln(_name);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
auto s = new StorableShape("first", "second");
|
||||
s.print(); // StorableShape._name
|
||||
s._store.print(); // StorableShape.MyDBObject._name
|
||||
s.saveState(); // StorableShape._name and StorableShape.MyDBObject._name
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import std.array;
|
||||
|
||||
interface Stack(T)
|
||||
{
|
||||
@property bool empty();
|
||||
@property ref T top();
|
||||
void push(T value);
|
||||
void pop();
|
||||
}
|
||||
|
||||
class StackImpl(T) : Stack!T
|
||||
{
|
||||
private T[] _store;
|
||||
|
||||
@property bool empty()
|
||||
{
|
||||
return _store.empty;
|
||||
}
|
||||
|
||||
@property ref T top()
|
||||
{
|
||||
assert(!empty);
|
||||
return _store.back;
|
||||
}
|
||||
|
||||
void push(T value)
|
||||
{
|
||||
_store ~= value;
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
assert(!empty);
|
||||
_store.popBack();
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
auto stack = new StackImpl!int;
|
||||
assert(stack.empty);
|
||||
stack.push(3);
|
||||
assert(stack.top == 3);
|
||||
stack.push(5);
|
||||
assert(stack.top == 5);
|
||||
stack.pop();
|
||||
assert(stack.top == 3);
|
||||
stack.pop();
|
||||
assert(stack.empty);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import std.stdio;
|
||||
|
||||
class A
|
||||
{
|
||||
int x = 42;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
{
|
||||
auto a1 = new A;
|
||||
assert(a1.x == 42);
|
||||
auto a2 = a1;
|
||||
a2.x = 100;
|
||||
assert(a1.x == 100);
|
||||
}
|
||||
{
|
||||
auto a1 = new A;
|
||||
auto a2 = new A;
|
||||
a1.x = 100;
|
||||
a2.x = 200;
|
||||
// Заставим a1 и a2 обменяться привязками
|
||||
auto t = a1;
|
||||
a1 = a2;
|
||||
a2 = t;
|
||||
assert(a1.x == 200);
|
||||
assert(a2.x == 100);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import std.math;
|
||||
|
||||
class Test
|
||||
{
|
||||
double a = 0.4;
|
||||
double b;
|
||||
|
||||
this(int b)
|
||||
{
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
this() {}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto t = new Test(5);
|
||||
auto z = new Test;
|
||||
|
||||
assert(t.b == 5);
|
||||
assert(isNaN(z.b));
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import std.math;
|
||||
|
||||
class Test
|
||||
{
|
||||
double a = 0.4;
|
||||
double b;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
// Объект создается с помощью выражения new
|
||||
auto t = new Test;
|
||||
assert(t.a == 0.4 && isNaN(t.b));
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
class Widget
|
||||
{
|
||||
this(Widget source)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Widget duplicate()
|
||||
{
|
||||
return new Widget(this); // Выделяет память и вызывает this(Widget)
|
||||
}
|
||||
}
|
||||
|
||||
class TextWidget : Widget
|
||||
{
|
||||
this(TextWidget source)
|
||||
{
|
||||
super(source);
|
||||
}
|
||||
|
||||
override TextWidget duplicate()
|
||||
{
|
||||
return new TextWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
TextWidget tw;
|
||||
TextWidget clone = tw.duplicate();
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
class Contact
|
||||
{
|
||||
string bgColor()
|
||||
{
|
||||
return "Серый";
|
||||
}
|
||||
}
|
||||
|
||||
class Friend : Contact
|
||||
{
|
||||
string currentBgColor = "Светло-зеленый";
|
||||
string currentReminder;
|
||||
|
||||
this(ref string c)
|
||||
{
|
||||
currentBgColor = c;
|
||||
}
|
||||
|
||||
override string bgColor()
|
||||
{
|
||||
return currentBgColor;
|
||||
}
|
||||
|
||||
string reminder()
|
||||
{
|
||||
return currentReminder;
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
string startColor = "Синий";
|
||||
Friend f = new Friend(startColor);
|
||||
Contact c = f; // Подставить экземпляр класса Friend вместо экземпляра класса Contact
|
||||
auto color = c.bgColor(); // Вызвать метод класса Friend
|
||||
|
||||
import std.stdio : writeln;
|
||||
writeln(color);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module test;
|
||||
|
||||
class Widget {}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert((new Widget).toString() == "test.Widget");
|
||||
}
|
Loading…
Reference in New Issue