Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Podbelsky_V_V_C_Bazovy_kurs.pdf
Скачиваний:
69
Добавлен:
02.06.2015
Размер:
1.73 Mб
Скачать

200

Г л а в а 1 1

 

 

Для краткости здесь только текст функции Main(). Предполагается, что и определение класса Counter и функция Main() принадлежат одному пространству имен.

Результаты выполнения программы:

Reading: 0 maxCount: 100000 c1.ToString(): Counter c1.GetType(): Counter c1.getCount() = 50000

Reading: 15000 maxCount: 100000

В теле функции Main() оператор Counter c1 = new Counter(); объявляет переменную с1 – ссылку типа Counter. Выражение с операцией new представляет собой обращение к конструктору умолчания, неявно включенному компилятором в класс Counter. Тем самым создан объект класса Counter, и с ним связана ссылка с1. Аналогично определяется второй объект и связанная с ним ссылка с2.

Оператор с1.display(); – это обращение к нестатическому методу display(), который вызывается для обработки объекта, связанного со ссылкой с1. В результате выполнения метода на консоль выводится строка, представляющая значения поля объекта и статического поля maxCount:

Reading: 0 maxCount: 100000

Вид представления определяет форматная строка метода Console.WriteLine(), вызываемого в теле метода display(). Так как при создании объекта использован конструктор умолчания Counter(), то переменные (поля) объекта получили значения за счет их инициализации (count=0 и maxCount=100000).

Следующие два оператора метода Main() иллюстрируют применимость к объектам введенного программистом-пользо- вателем класса Counter, методов, унаследованных этим классом от класса object. Как мы уже говорили, класс object является первичным базовым классом всех классов программ на C#. Применяемые к объектам пользовательского класса Counter методы ToString() и GetType() позволяют получить имя этого класса.

В цикле с заголовком for (int i = 0; i < 150000; i++) выполняются обращения к методу incrementM() для двух объектов

Классы как типы

201

 

 

класса, адресуемых ссылками с1 и с2. Для первого из них выполняется 150000 обращений, для второго – в 10 раз меньше. Метод при каждом обращении увеличивает текущее значение счетчика на 1, но «следит» за переполнением. После 99999 обращений счетчик, связанный со ссылкой с1, будет обнулен. Приведенные результаты иллюстрируют изменения объектов.

Прежде чем завершить пояснения особенностей определенного нами класса и возможностей работы с его объектами, отметим, что в функции Main() используются только открытые методы класса Counter. В объявлениях методов increment(), getCount() и display() явно использован модификатор public. Конструктор умолчания Counter() создается компилятором автоматически как открытый. Именно названные четыре метода класса Counter формируют его внешний интерфейс. Обращения к закрытым полям класса невозможны. Если попытаться использовать вне класса, например, такой оператор:

Console.Write ("count = {0 }", c1.count);

то компилятор сообщит об ошибке:

Error1 ‘ Counter.count’ is inaccessible due to its protection level

11.5. Ссылка this

Нестатические методы класса отличаются от статических наличием в первых ссылки this. Эта ссылка позволяет нестатическому методу “узнать”, для какого объекта метод вызван и выполняется в данный конкретный момент. Значением ссылки this является ссылка на тот объект, обработка которого выполняется. Ссылку this нельзя и нет необходимости определять – она всегда автоматически включена в каждый нестатический метод класса и связана с тем объектом, для которого метод вызывается. Иногда ссылку this называют дополнительным параметром нестатического метода. Этот параметр представляет в методе вызвавший его объект. Можно назвать следующие четыре вида возможных применений ссылки this.

1. Для избежания конфликта имен между параметрами метода и членами объекта.

202

Г л а в а 1 1

 

 

2.Для организации связей между объектами одного класса.

3.Для обращения из одного конструктора класса к другому конструктору того же класса.

4.Для определения расширяющего метода (см [12] – 1360;

[1]).

Продемонстрируем первые две названные возможности применения ссылки this на следующем примере.

Определим класс Link, объекты которого можно объединять в цепочку – в односвязный список. Начало списка, т. е. ссылку на первый объект, включенный в список, представим статическим полем класса (static Link beg). Когда список пуст, значением beg будет null.

Программа с указанным классом:

// 11_05.cs this и связный список using System;

class Link

{

static Link beg; Link next;

int numb;

public void add(int numb)

{

this.numb = numb;

if (beg == null) // Список пуст { beg = this; return; }

Link temp = beg;

while (temp.next != null) temp = temp.next;

temp.next = this;

}

static public void display()

{

Link temp = beg; while (temp != null)

{

Console.Write(temp.numb + " "); temp = temp.next;

}

Console.WriteLine();

}

Классы как типы

203

 

 

}

class Program

{

static void Main()

{

Link a = new Link(), b = new Link(), c = new Link(); a.add(7);

b.add(-4); c.add(0); Link.display();

}

}

Результат выполнения программы:

7 -4 0

Нестатическими полями класса являются int numb и Link next. Первое поле нужно нам только для иллюстрации, значением второго поля будет ссылка на следующий объект, подключенный к списку вслед за рассматриваемым. Последовательность формирования списка из объектов класса Link показана на рис. 11.1

Рис.11.1. Формирование списка объектов

Для создания объектов класса Link в программе использован конструктор без параметров, который автоматически встраивается в класс, когда в нем нет явно определенных конструкторов.

Для «подключения» объекта к списку введен нестатический открытый метод:

public void add(int numb)

{

204

Г л а в а 1 1

 

 

this.numb = numb;

if (beg == null) // Список пуст { beg = this; return; }

Link temp = beg;

while (temp.next != null) temp = temp.next;

temp.next = this;

}

Особенность метода – имя его параметра, совпадает с именем поля класса. Полное (квалифицированное) имя this. numb относится только к полю объекта. Первое действие метода

– присваивание полю this.numb значения параметра с тем же именем. Применение ссылки this позволяет отличать поле объекта, для которого вызван метод add(), от одноимённого параметра.

По умолчанию статическое поле Link beg и нестатическое поле Link next инициализируются значениями null. Пока список пуст поле beg сохраняет это пустое значение. Полю beg присваивается ссылка на первый объект класса Link, для которого будет вызван метод add(). Если в списке уже есть элементы (объекты класса Link), то создается локальная переменная Link temp, инициализированная значением ссылки beg. Далее в цикле перебираются все элементы списка пока не будет найден элемент, которому никакой объект не подключен. Его полю, доступ к которому обеспечивает квалифицированное имя temp.next, присваивается значение ссылки this, т.е. ссылка на очередной объект класса Link, для которого вызван метод add().

Для последовательного перебора элементов списка и вывода значений полей numb определён статический метод:

static public void display()

{

Link temp = beg; while (temp != null)

{

Console.Write(temp.numb+" "); temp = temp.next;

}

Console.WriteLine();

}

Классы как типы

205

 

 

В теле метода цикл, в котором локальная переменная temp (ссылка на объекты класса Link) последовательно адресует объекты списка, начиная с того, который ассоциирован со статической переменной beg.

Для иллюстрации возможностей класса Link в классе Program определён метод Main():

static void Main()

{

Link a = new Link(), b = new Link(), c = new Link(); a.add(7);

b.add(-4); c.add(0); Link.display();

}

Если в классе определены несколько нестатических конструкторов, то зачастую удобно из одного конструктора обращаться к другому. Эту возможность, как отмечено ранее, обеспечивает применение ссылки this. Перед тем, как рассмотреть эту роль служебного слова this, рассмотрим нестатические конструкторы.

11.6. Конструкторы объектов класса

Недостаток рассмотренных выше класса чисел и класса Link

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

Конструктор объектов (иначе экземпляров) класса – это метод класса, в котором реализованы все действия, необходимые для инициализации объекта класса при его создании. Для простоты выражение «конструктор объектов (иначе экземпляров) класса» обычно сокращают и говорят просто о конструкторе. (Обратите внимание на то, что этот термин не применяют к статическому конструктору.)

Внешне любой конструктор – это метод класса, имя которого всегда совпадает с именем самого класса. Формат объявления конструктора:

206

Г л а в а 1 1

 

 

модификаторы_конструктораopt имя_конструктора (спецификация_параметровopt) инициализатор_конструктораopt тело_конструктора

Имя конструктора – имя именно того класса, которому конструктор принадлежит, других имен у конструктора не может быть.

Обратите внимание на то, что у конструктора отсутствует тип возвращаемого значения, даже тип void в качестве типа возвращаемого значения для конструктора недопустим.

В качестве модификаторов конструктора используются: public, protected, internal, private, extern

Первые четыре модификатора — это модификаторы доступа. Их назначение мы уже рассмотрели. О назначении модификатора extern мы уже упоминали в связи с методами класса.

Спецификация параметров конструктора может отсутствовать, но если она есть, то содержит спецификации всех параметров конструктора.

Тело конструктора – это: либо блок – последовательность операторов, заключенная в фигурные скобки, либо пустой оператор, обозначаемый только отдельным символом точка с запятой. Пустой оператор в качестве тела используется только для конструкторов с модификатором extern. Мы будем в качестве тела конструктора использовать блок.

Назначение операторов тела конструктора – выполнить инициализацию создаваемого объекта класса.

Инициализатор_конструктора – это специальное средство, позволяющее до выполнения операторов тела конструктора обратиться к конструктору базового класса или к другому конструктору этого же класса. Для обращения к конструктору базового класса используется выражение:

: base (список_аргументовopt)

Здесь base – служебное слово; необязательный список аргументов – список выражений, соответствующих параметрам вызываемого конструктора базового класса.

Инициализатор конструктора, выполняющий обращение к другому конструктору своего же класса, выглядит так:

: this (список_аргументовopt)

Классы как типы

207

 

 

Здесь this – служебное слово; необязательный список аргументов – список выражений, соответствующих параметрам другого конструктора.

Хотя язык C# с помощью механизма сбора мусора (garbage collection) в достаточной мере защищен от таких проблем как «утечка памяти» и «висячие ссылки», однако в C# остается, например, задача глубокого копирования, и важным вопросом в ряде случаев является приведение типов, введенных пользовательскими классами. Зачастую для квалифицированного решения названных проблем можно пользоваться конструкторами разных видов. В классе могут быть явно объявлены:

конструктор умолчания (конструктор без параметров); конструктор копирования; конструктор приведения типов; конструктор общего вида.

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

– его требуется явно включать в объявления класса. Формат объявления:

class CLASS { public CLASS() {

операторы_тела_конструктораopt

}

}

Конструктор копирования – это конструктор с одним параметром, тип которого – ссылка на объект своего класса. Такой конструктор по умолчанию не создается, а зачастую нужно в программе иметь возможность создать точную копию уже существующего объекта. Присваивание ссылке с типом класса значения ссылки на существующий объект того же класса копии объекта не создает. Две ссылки начинают адресовать один и

208

Г л а в а 1 1

 

 

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

// 11_06.cs конструкторы using System;

class CL{

public int dom = 6;

public CL() { } // Конструктор умолчания public CL(CL ob) { // Конструктор копирования

dom = ob.dom;

}

}

class Program { static void Main() {

CL one = new CL(); CL two = new CL(one); two.dom = 5*one.dom;

Console.WriteLine("one.dom="+one.dom+", two. dom="+two.dom);

}

}

Результат выполнения программы:

one.dom=6, two.dom=30

В классе CL конструктор без параметров объявлен явно. Для простоты примера в классе CL всего одно открытое поле int dom. В методе Main() ссылка one связана с объектом, инициализированным конструктором без параметров. Объект, адресуемый ссылкой two – копия объекта one, но эти объекты независимы, что иллюстрируют результаты выполнения программы.

Особенности обращений конструкторов одного класса друг к другу расмотрим на примере класса, особым образом представляющего вещественные числа. В научной записи (в научной нотации) число записывается в виде произведения двух чисел: целой степени p числа 10 и числа m, удовлетворяющего условию 1.0 <= m < 10.0. Иногда p называют показателем, а m – мантиссой числа. Таким образом, запись каждого числа выглядит так: m*10p. (При выводе значения числа в научной

Классы как типы

209

 

 

нотации возведение в степень будем обозначать символом ^.). Примеры постоянная Авогадро: 6.02486 * 1023 (г*моль)-1,

заряд электрона: -4.80286 * 10-10 СГСЭ.

Определим класс Real для представления чисел в научной нотации. В классе определим метод display() для вывода на консоль значения объекта и метод incrementM() для увеличения на 1 значения мантиссы числа. При этом значение числа увеличивается, конечно, не на 1, а на 10p. Для мантисс и показателей введем закрытые поля double m и int p. Определение класса может быть таким (программа 11_07.cs):

using System;

class Real // Класс чисел в научной нотации

{// Закрытые поля:

double m = 8.0; // Мантисса явная инициализация

int p;

// Порядок инициализация по умолчанию

//Метод приращение мантиссы: public void incrementM()

{

m += 1;

if (m >= 10)

{

m /= 10; p++;

}

}

//Метод для вывода значения числа (объекта): public void display(string name)

{

string form = name + "\t = {0,8:F5}*10^{1,-3:D2}"; Console.WriteLine(form, m, p);

}

//Конструктор общего вида:

public Real(double mi, int pi)

{

m = mi; p = pi; reduce();

}

// Конструктор приведения типов:

210

Г л а в а 1 1

 

 

public Real(double mi) : this(mi, 0)

{ } // Конструктор копирования:

public Real(Real re) : this(re.m, re.p)

{ }

//Конструктор умолчания: public Real()

{ }

//"Внутренний" для класса метод:

void reduce() // Приведение числа к каноническому виду.

{

double sign = 1; if (m < 0) {sign = -1; m = -m; } for (; m >= 10; m /= 10, p += 1) ;

for (; m < 1; m *= 10, p -= 1) ; m *= sign;

}

}

Среди методов класса нам сейчас важно рассмотреть явно определенные в классе конструкторы.

Конструктор общего вида:

public Real(double mi, int pi)

{

m = mi; p = pi; reduce();

}

Параметры определяют значения мантиссы m и порядка p создаваемого объекта класса. В соответствии с правилами записи чисел в научной нотации для них необходимо соблюдение условия:

1.0 <= m < 10.0.

Так как значения аргумента mi при обращении к конструктору может не удовлетворять этому условию, то в теле конструктора вызывается закрытый для внешних обращаний метод reduce(). Его задача – нужным образом преобразовать значения полей m и p.

Классы как типы

211

 

 

Конструктор приведения типов:

public Real(double mi) : this(mi, 0) { }

Это частный случай конструктора общего вида с одним параметром. В нашем примере он формирует объект класса Real по одному значению типа double, использованному в качестве аргумента. Тем самым этот конструктор позволяет преобразовать числовое значение в объект класса Real. В конструкторе применен инициализатор, содержащий обращание this(mi, 0) к конструктору с заголовком Real(double mi, int pi). Значение второго аргумента, определяющего значение поля int p, задано нулевой константой, что соответствует естественному для математики способу записи вещественного числа.

Констуктор копирования:

public Real(Real re) : this(re.m, re.p) { }

Позволяет создать копию объекта. Ещё раз обратим внимание на его отличие от операции присваивания, применение которой копирует только значение ссылки на объект. После присваивания ссылок на один объект начинают указывать несколько переменных (ссылок). Тело конструктора копирования в нашем примере не содержит операторов. Для присваивания значений полям создаваемого объекта используется инициализатор конструктора, содержащий обращение this(re.m, re.p) к конструктору общего вида. Вместо инициализатора можно было бы присваивать значения переменным m и p в теле конструктора. (Конструктор копирования по умолчанию не создается.)

Конструктор умолчания, т.е. конструктор без параметров:

public Real() { }

Отсутствие параметров, отсутствие (в данном примере) инициализатора конструктора и пустое тело конструктора вызывает вопрос: «А зачем нужен такой конструктор?» Ведь в предыдущем варианте класса Real объявления такого конструктора не было.

При наличии явно определенных конструкторов (хотя бы одного) компилятор не встраивает в определение класса конструктор с пустым списком параметров. При необходимости

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]