Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Правила доступа к элементам базового класса при наследовании



Таблица определяет возможные варианты доступа к элементам базового класса из элементов-функций производного клас­са и из прикладной программы, использующей объекты производного класса.

Таблица. Атрибуты доступа к элементам базового класса

Доступ в базовом классе Тип базового класса Доступ из произ­водного класса Доступ пользователей производного класса
public protected private public protected private public public public private private private public protected недоступен private private недоступен доступен недоступен недоступен недоступен недоступен недоступен

Программа на рис. 3 демонстрирует работу правил доступа, представленных в таблице, для случая, когда класс Base является общедоступным базовым классом.

При использовании приватного базового класса дополнительно к правилам, приведенным в таблице, для protected и public элементов базового класса можно восстановить их уровень доступа и в произ­водном классе, если использовать дополнительные объявления этих элементов в производном классе (рис. 4). Отметим, что по умолчанию базовый класс наследуется как приватный.

 

#include < iostream>

using namespace std;

class Base {

private:

int privateMember;

protected:

int protectedMember;

public:

int publicMember;

};

class Derived: public Base {

public:

void f(){ cout< < privateMember; } // ошибка

void g(){ cout< < protectedMember; }

void h(){ cout< < publicMember; }

};

void main(){

Derived derived;

derived.privateMember=1; // ошибка

derived.protectedMember=2; // ошибка

derived.publicMember=3;

}

Рис. 3. Файл Derived1.cpp

 

#include < iostream>

using namespace std;

class Base {

private:

int privateMember;

protected:

int protectedMember1, protectedMember2;

public:

int publicMember1, publicMember2;

};

class Derived: Base {

protected:

Base:: protectedMember2;

public:

Base:: publicMember2;

void f(){ cout< < privateMember; } //ошибка

void g(){ cout< < protectedMember1< < protectedMember2; }

void h(){ cout< < publicMember1< < publicMember2; }

};

void main(){

Derived derived;

derived.privateMember=1; // ошибка

derived.protectedMember2=2; // ошибка

derived.publicMember1=3; // ошибка

derived.publicMember2=4;

}

Рис. 4. Файл Derived2.cpp

Явные и неявные преобразования типов при наследовании

При использовании наследования объект производного типа мо­жет рассматриваться как объект его базового типа; обратное ут­верждение неверно. Для общедоступного базового класса компилятор может неявно выполнить преобразование объекта производного типа к объекту базового типа:

class Base { /*...*/);

class Derived: public Base { /*..*/};

void main() {

Derived derived;

Base base=derived;

...

Обратное преобразование - Base к Derived - должно быть опре­делено программистом посредством конструктора Derived (Base):

Derived tmp=base; // вызов конструктора

Отметим, что размеры объектов производного и базового типов обычно различны. Поэтому удобнее использовать не объекты, а ука­затели (или ссылки) на них, так как все указатели имеют одинако­вый размер. Кроме этого, в качестве элементов контейнерных клас­сов чаще всего используются не объекты, а указатели на них. Поэ­тому при наследовании наиболее актуальными являются преобразова­ния типов указателей.

Если базовый класс является общедоступным базовым классом, то для пользователей производного и базового типов принципы пре­образования указателей очень просты: неявно может быть выполнено преобразование указателя типа Derived* к указателю Base*; обрат­ное преобразование обязательно должно быть явным, например:

 

class Base {/*...*/};

class Derived: public Base {/*...*/};

void main(){

Derived derived;

Base *bp=& derived; // неявное преобразование

Derived *dp_1=bp; // ошибка

Derived *dp_2=(Derived*)bp; // явное преобразование

}

 

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

Использование указателей на базовый класс. Виртуальные функции

Концепция " то же, что и" - мощный механизм абстракции, так как она позволяет во многих контекстах пользоваться производным классом как базовым. Например, если функция print(Base*) распеча­тывает определенные поля объектов базового класса, то ее можно использовать и для объектов производного класса, например:

Derived d;

...

print(& d);

Однако использование указателей на базовый класс для указа­ния на объект производного класса приводит к тому, что через та­кой указатель будет осуществляться доступ только к функциям-эле­ментам базового класса. Другими словами, выбор функций-элементов класса будет определяться типом указателя, а не типом объекта, на который в данный момент указывает указатель.

Программа Fruit.cpp (рис. 5) демонстрирует использование ука­зателей типа fruit* для указания на объекты порожденных классов apple и orange. В результате на экран будет трижды выведено слово Fruit, поскольку в цикле for будет трижды вызываться функция fru­it:: identify().

 

#include < iostream>

using namespace std;

class fruit{

public:

char *identify(){ return " Fruit"; }

};

class apple: public fruit{

public:

char *identify(){ return " Apple"; }

};

class orange: public fruit{

public:

char *identify(){ return " Orange"; }

};

void main()

{

fruit* f_ptr[3];

f_ptr[0]=new fruit;

f_ptr[1]=new apple;

f_ptr[2]=new orange;

for(int i=0; i< 3; i++)

cout< < f_ptr[i]-> identify();

}

 

Рис. 5. Файл Fruit.cpp

 

Таким образом, элемент-функция вызывается в зависимости от типа указателя или ссылки, использованной для доступа к ней, а не от текущего типа объекта, на который ссылается указатель или ссылка. Это еще раз демонстрирует следующий пример:

fruit *fp=new apple;

fp-> identify(); // возвращает " Fruit"

((apple*)fp)-> identify(); // возвращает " Apple"

apple *ap=(apple*)fp;

ap-> identify(); // возвращает " Apple"

Программа Fruit.cpp должна правильно идентифицировать фрук­ты, а для этого необходимо, чтобы соответствующая идентифицирую­щая функция определялась типом текущего объекта, а не типом ссыл­ки или указателя, использованных для доступа к нему. Данная цель будет достигнута, если функцию identify() определить в базовом классе как виртуальную:

class fruit {

public:

virtual char *identify() { return " Fruit"; )

};

С добавлением ключевого слова virtual в объявлении fru­it:: identify() программа Fruit.cpp будет работать так, как мы хо­тели, печатая слова - Fruit, Apple, Orange.

Отметим, что функции apple:: identify и orange:: identify так­же являются виртуальными, хотя мы их явно не объявляем. Правило для определения, когда функция виртуальная, следующее: функция является виртуальной, если она объявлена виртуальной, или есть функция из базового класса с той же сигнатурой, которая является виртуальной. Сигнатура функции состоит из ее имени и типов фор­мальных аргументов.

Использование виртуальных функций приводит к тому, что на этапе компиляции невозможно будет определить, какая виртуальная функция (базового или производного класса) будет выполняться. Этот выбор будет производиться на этапе выполнения программы и он носит название позднего (динамического) связывания имен виртуаль­ных функций.

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

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

 

ОБОРУДОВАНИЕ

Персональный компьютер: процессо­р с частотой 1, 6ГГц или выше, 1024 МБ ОЗУ, жесткий диск со скоростью 5400 об/мин, видеоадаптер с поддержкой DirectX 9 и с разрешением 1280 х 1024 (или более высоким), операционная система Windows 7, интегрированные среды разработки приложений Visual Studio 2010 и Visual Studio Team System 2008 с комплектами документации MSDN, каталог Tprog\Lab5, содержащий исходные файлы проектов в подкаталогах Arr_bnd (Array.h, Array.cpp, Arrbnd.h, Arrbnd.cpp, Arrbprog.cpp) и Derived (Derived1.cpp, Derived2.cpp, Fruit.cpp), файл Labtprog5.doc (методи­ческие указания к данной лаборатор­ной работе), не менее 200 Mб свободной памяти на логическом диске, со­держащем каталог Tprog\Lab5.

 

ЗАДАНИЕ НА РАБОТУ

 

4.1. Ознакомиться с технологией включения и наследования на неуправляемом (unmanaged) языке Visual С++ в интегрированной среде разработки приложений Visual Studio 2010 в процессе создания приложений Arr_bnd, Derived1, Derived2, Fruit.

4.2. Разработать и отладить объектно-ориентированную программу на неуправляемом (unmanaged) языке С++ в интегрированной среде разработки приложений Visual Studio 2010 в соответствии с заданием преподавателя. Примерами заданий могут быть следующие.

1. Написать тексты h-файлов и cpp-файлов для базового класса Point и производного класса Rect (прямоугольник). Описание классов:

Класс Элементы данных Интерфейс
Point x, y Конструкторы, функции move, assign, print
Rect dx, dy Конструкторы, функция square, операции =, +=, < <

Разработать и отладить программу с примерами создания и использования объектов классов Point и Rect.

2. Написать тексты h-файлов и cpp-файлов для базового класса Point и производного класса Rect (прямоугольник). Описание классов:

Класс Элементы данных Интерфейс
Point x, y Конструкторы, функции move, print, операции =, +=, ==
Rect p2(типа Point) Конструкторы, функции move, square, операции =, <, < <

Разработать и отладить программу с примерами создания и использования объектов классов Point и Rect.

3. Написать тексты h-файлов и cpp-файлов для базового класса Point и производного Circle (окружность). Описание классов:

Класс Элементы данных Интерфейс
Point x, y Конструкторы, операции +=, =, < <
Circle r Конструкторы, friend-функции move, square, операции =, ==, print

Разработать и отладить программу с примерами создания и использования объектов классов Point и Circle.

4. Разработать базовый класс Point и производный класс Rect (прямоугольник)., позволяющие использовать их в следующей программе:

Point p1, p2(3, 11), p3=p2; (p2.move(10, 10)).print(); p3.print(); p1=p2;

Rect r1, r2(p1, p2), r3=r2; cout < < r3< < square(r3); r2=r3.move(5, 8);

Написать тексты h-файла и cpp-файла для классов Point и Rect. Разработать и отладить программу создания и использования объектов классов Point и Rect.

5. Разработать базовый класс Point и производный класс Circle (окружность), позволяющие использовать их в следующей программе:

Point p1(10, 20), p2; p2=2*p1 + Point(20, 30); cout< < (p1=p2);

Circle r1, r2(p1, p2), r3=r2; cout < < r3< < square(r3); r2=r3.move(5, 8);

Написать тексты h-файла и cpp-файла для классов Point и Circle. Разработать и отладить программу создания и использования объектов классов Point и Circle.

 

ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ

 

5.1. Проверить наличие на компьютере необходимого аппаратного оборудования и программного обеспечения, наличие 200 Мб свободной памяти на логическом диске, содержащем каталог Tprog\Lab5, наличие файла Labtprog5.doc и исходных файлов в подкаталогах Arr_bnd (Array.h, Array.cpp, Arrbnd.h, Arrbnd.cpp, Arrbprog.cpp) и Derived (Derived1.cpp, Derived2.cpp, Fruit.cpp) каталога Tprog\Lab5.

5.2. Создать личный каталог, в котором будут размещаться создаваемые во время лабораторной работы проекты.

5.3. Создать в личном каталоге пустой проект неуправляемого консольного приложения, выбирая в IDE-среде в меню File следующую последовательность опций: New-> Project-> Other Language -> Visual C++ -> Win32 -> Win32 Console Application -> Empty Project, а затем добавить в него исходные файлы одного из каталогов, предварительно выполнив копирование их в каталог проекта. По команде Ctrl+F5 откомпилировать проект и выполнить приложение. Проверить правильность работы приложения.

5.4. Повторить выполнение пункта 5.3 для остальных приложений.

5.5. Разработать и отладить объектно-ориентированную программу на неуправляемом (unmanaged) языке С++ в интегрированной среде разработки приложений Visual Studio 2010 в соответствии с заданием преподавателя. Если при отладке возникают проблемы с устранением ошибок в программе, необходимо выделить ошибку в окне Error List и нажать клавишу F1. В появившемся окне документации MSDN будет приведены примеры исправления ошибки.

 

ОФОРМЛЕНИЕ ОТЧЕТА

 

Отчет должен содержать:

· цель работы и индивидуальное задание;

· тексты исходных файлов, содержащие описание и реализацию классов, используемых в лабораторной работе;

· файлы *.h и *.cpp, содержащие описание и реализацию классов в соответствии с заданием преподавателя;

· текст тестовой программы и результаты работы программы;

· краткая информация о механизмах включения и наследования.

 

КОНТРОЛЬНЫЕ ВОПРОСЫ

 

7.1. В чем заключается наследование? Как реализуется расши­рение или специализация интерфейса базового класса?

7.2. В каком порядке производится инициализация данных при порождении объекта производного класса?

7.3. Как производится наследование операторных функций базо­вого класса классом Arr_bnd?

7.4. Почему необходима перегрузка оператора индексации [] для класса Arr_bnd?

7.5. Какие правила существуют для доступа к элементам базо­вого класса из элементов-функций производного класса и из прик­ладной программы, использующей объекты производного класса?

7.6. Какие правила существуют для явного и неявного преобра­зования типов данных при наследовании?

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

 

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

 

1. Подбельский, В.В. Язык Си+: учеб.пособие для вузов / В.В.Подбельский.— 5-е изд. — М.: Финансы и статистика, 2007 (2001, 2002, 2003, 2004, 2005, 2006).— 560с.: ил.

2. Павловская, Т.А. C/C++: Программирование на языке высокого уровня: учебник для вузов / Т.А.Павловская.— М.[и др.]: Питер, 2007 (2002, 2003, 2004, 2005, 2006).— 461с.: ил.3. Гарнаев А.Ю. Самоучитель Visual Studio.Net 2003. – СПб.: БХВ-Петербург, 2003. – 688 с.

3. Шилдт, Г. C+: базовый курс / Г.Шилдт; пер.с англ.и ред.Н.М.Ручко.— 3-е изд. — М.[и др.]: Вильямс, 2007 (2005).— 624с.: ил.5. Уоткинз Д., Хаммонд М., Эйбрамз Б. Программирование на платформе.NET. – М.: Издательский дом " Вильямс", 2003. – 368 с.

4. MSDN 2010. Электронная документация Microsoft для разработчиков программного обеспечения. – 500000 с.

5. Пол Айра. Объектно-ориентированное программирование с использованием языка С++: Пер. с англ.- К.: НИПФ " ДиаСофтЛтд.", 1998. - 480 с.

6. Г. Шилдт. Теория и практика С++: Пер. с англ. – СПб.: BHV – Санкт-Петербург, 1999. – 416 с.

7. Цимбал А.А., Майоров А.Г., Козодаев М.А. Turbo C++: Пер. с англ.-М.: Джен Ай Лтд, 1993.- 512с.

8. С.Дьюхарст, К.Старк. Программирование на С++: Пер. с англ.- Киев: " ДиаСофт", 1993. - 272с.

 

 


ЛАБОРАТОРНАЯ РАБОТА N 6

Объектно-ориентированное программирование на языке C# 2.0.

 

ЦЕЛЬ И ЗАДАЧИ РАБОТЫ

 

Изучение и практическое освоение приемов и средств объектно-ориентированного программирования на базе языка C# и инструментальной среды разработки MS Visual Studio.NET 2010.

 

ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ

 

Исполняющая среда для языка C#

 

На языке C# создаются управляемые приложения, которые выполняются внутри Microsoft CLR. Среда CLR (Common Language Runtime — общеязыковая исполняющая среда) — это виртуальная исполняющая система (Virtual Execution System — VES), реализующая CLI (Common Language Infrastructure – общая инфраструктура языка). В CLR предоставляется множество удобных средств для управляемых приложений, включая, помимо прочего, четко налаженный сборщик мусора для управления памятью, уровень безопасности доступа к коду, развитую самоописательную систему типов.

В стандарт CLI входит также файловый формат РЕ (Portable Executable — переносимый исполняемый модуль) для управляемых модулей. Таким образом, в действительности, можно скомпилировать программу С# на платформе Windows, а выполнять и на Windows, и на Linux, причем без перекомпиляции, поскольку даже формат файлов является стандартизованным.

Чтобы обеспечить функциональную совместимость между разными языками, в CLI определено подмножество общей системы типов (Common Type System — CTS), которое называется спецификацией общего языка (Common Language Specification — CLS).

Среда CLR — это не интерпретатор. Она не производит повторную трансляцию кода IL при каждом его выполнении. CLR в действительности компилирует код IL в машинный код, прежде чем выполнять его, т.е. осуществляет JIT-компиляцию.

Сборка (assembly) — это дискретная единица многократно используемого кода внутри CLR. По своей природе она подобна DLL-библиотеке из мира неуправляемого кода, но на этом все сходство заканчивается. Сборка может состоять из множества модулей, которые объединяет вместе манифест, описывающий содержимое сборки. С точки зрения операционной системы, модуль идентичен DLL-библиотеке. Сборки могут иметь присоединенные к ним версии, что позволяет идентифицировать одноименные сборки, но с разными версиями, как отдельные. Сборки также содержат метаданные, описывающие содержащиеся в них типы. При поставке " родной" DLL-библиотеки, написанной на неуправлямом языке, в нее обычно включается заголовочный файл и/или документация, описывающая экспортированные функции. Метаданные удовлетворяют этим требованиям, полностью описывая типы, содержащиеся внутри сборки. Короче говоря, сборки — это снабженные версиями, самоописательные единицы многократно используемого кода в среде CLR.

Именовать сборки можно двумя основными способами.

  • Строгое (полное) именование. Такая сборка имеет имя, состоящее из четырех частей: краткое имя сборки, номер версии, идентификатор культуры в формате ISO и хеш-маркер. Если имя сборки состоит из всех четырех частей, она считается строго именованной.
  • Частичное именование. Такая сборка имеет имя, в котором опущены некоторые детали строго именованной сборки.

Преимущество строго именованных сборок состоит в том, что они могут быть зарегистрированы в глобальном кэше сборок (Global Assembly Cache — GAC) и стать доступными для использования всеми приложениями системы. Регистрация сборки в GAC аналогична регистрации в системном реестре СОМ-сервера. Если сборка не является строго именованной, то приложение может использовать ее лишь локально. Другими словами, сборка должна находиться где-то в каталоге приложения, использующего ее, либо в его подкаталогах. Такие сборки часто называют приватными.

Версии играют ключевую роль во время загрузки сборок, и все сборки снабжены информацией о версии. Механизм контроля версий был встроен в загрузчик CLR изначально, и это исключает такую неприятность, которая называется адом DLL (DLL Hell), когда замена совместно используемой DLL-библиотеки ее новой версией приводит к нарушению работы приложений, пользующихся старой версией этой библиотеки.

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

Метаданные — это расширяемый формат описания содержимого сборок. С помощью метаданных можно получать доступ и исследовать определения типов и присоединенные к ним атрибуты. Метаданные могут сообщить о том, поддерживает ли определенный класс объекта конкретный метод, прежде чем пытаться вызвать его, либо порожден ли данный класс от какого-то другого. Процесс просмотра метаданных называется рефлексией. Обычно рефлексия типов в сборке начинается с объекта System.Type.

Получить один из этих экземпляров типов можно с помощью ключевого слова С# typeof, вызова System. Assembly. GetType () или нескольких других способов. В общем случае ключевое слово typeof более эффективно, потому что вычисляется во время компиляции, в то время как метод GetType (), хотя и является более гибким, поскольку ему можно передавать произвольную строку, запускается во время выполнения. После получения объекта типа можно определить, является ли он классом, интерфейсом, структурой и т.п., какие методы у него есть, а также количество и типы содержащихся в нем полей.

 

Определение классов в языке C#

 

Определения классов в С# похожи на определения классов в C++ и Java. Чтобы вы могли получить представление об этом, рассмотрим простейший класс. В следующем коде показаны базовые части, из которых состоит определение класса.

 

// ПРИМЕЧАНИЕ: этот код не предназначен для компиляции в том виде, как есть

[Serializable]

public class Derived: Base, ICloneable

{

private Derived( Derived other ) {

this.x = other.x;

}

public object Clone () { // реализует интерфейс IClonable.Clone

return new Derived( this );

}

private int x;

}

 

Это объявление класса определяет класс Derived, унаследованный от класса Base и также реализующий интерфейс ICloneable.

Модификатор доступа перед ключевым словом class управляет видимостью типа извне сборки. Класс Derived является общедоступным, а это означает, что пользователи сборки, содержащей этот класс, могут создавать его экземпляры. Этот тип содержит приватный (private) конструктор, используемый общедоступным (public) методом Clone, который реализует интерфейс ICloneable. Когда класс реализует интерфейс, это значит, что понадобится реализовать все методы этого интерфейса.

 

Почти к любой именованной сущности внутри системы типов CLR можно применять атрибуты. В данном случае атрибут Serializable был применен к классу, чтобы продемонстрировать пример использования синтаксиса атрибутов. Эти атрибуты становятся частью метаданных, описывающих тип для его потребителей. Вдобавок можно создавать собственные атрибуты для присоединения их к различным сущностям, таким как классы, параметры, возвращаемые значения и поля, что позволяет легко реализовать возможности аспектно-ориентированного программирования (Aspect Oriented Programming — АОР).

Поля (field) — это механизм, представляющий состояние объектов. Обычно новый класс объявляется только тогда, когда нужно смоделировать новый тип объекта, с собственным внутренним состоянием, представленным его полями экземпляра.

Поля объявляются с типом подобно тому, как это делается со всеми прочими переменными. Ниже перечислены допустимые модификаторы полей:

new

public

protected

internal

private

static

readonly

volatile

Инициализировать поля во время создания объекта можно различными способами.

Простейший способ сделать это — прибегнуть к помощи инициализаторов. Они задаются в точке определения поля и могут применяться как для статических полей, так и для полей экземпляра, например:

private int x = 789;

private int у;

private int z = A.InitZO;

Статические readonly-поля инициализируются в статическом конструкторе, а readonly-поля экземпляра — в конструкторе экземпляра. В качестве альтернативы такие поля можно инициализировать посредством инициализаторов в точке их объявления в классе, как это делается с другими полями. Внутри конструктора можно сколько угодно раз присваивать значения полям readonly. Только внутри конструктора поле readonly можно передать другой функции как параметр ref (параметр передается в функцию и возвращается) или out (возвращаемый параметр). Отметим, что по умолчанию параметр относится к параметру in (передаваемый в функцию). Рассмотрим пример:

public class A

{

public A()

{

this.у = 456;

// Можно даже еще раз установить у.

this.у = 654;

// Можно использовать у как параметр ref.

SetField( ref this.у );

}

private void SetField( ref int val )

{

val = 888;

}

private readonly int x = 123;

private readonly int y;

public const int z = 555;

static void Main()

{

A obj = new A ();

System.Console.WriteLine ( " x = {0}, у = {1}, z = {2}",

obj.x, obj.y, A.z );

}

}

Здесь следует отметить один важный нюанс: поле z объявлено с ключевым словом const. Поначалу может показаться, что эффект от этого будет тем же, что и от readonly, но на самом деле это не так. Во-первых, поле const известно и используется во время компиляции. Это значит, что код, сгенерированный компилятором в процедуре Main, может быть оптимизирован заменой всех случаев использования этой переменной непосредственным константным значением. Компилятор вправе предпринять такой трюк для повышения производительности — просто потому, что значение данного поля известно на момент компиляции. К тому же обратите внимание, что доступ к полю const осуществляется с указанием имени класса, а не имени экземпляра. Причина в том, что значения const являются неявно статическими и не влияют на отпечаток памяти или форму экземпляров объекта. Опять-таки, это имеет смысл, поскольку компилятор оптимизирует доступ к участку памяти в экземпляре объекта, поскольку это поле будет все равно одинаковым у всех экземпляров объекта.

Конструкторы вызываются при первоначальной загрузке класса средой CLR или при создании объекта. Существуют два типа конструкторов: статические конструкторы и конструкторы экземпляра. Класс может иметь только один статический конструктор, не имеющий параметров. Он вызывается, когда CLR загружает тип. Имя статического конструктора должно совпадать с именем класса, к которому он принадлежит. Как и к любому другому члену класса, к статическому конструктору можно присоединять атрибуты метаданных.

С другой стороны, конструкторы экземпляра вызываются при создании экземпляра класса. Обычно они устанавливают состояние объекта за счет инициализации полей в желательное предопределенное состояние. Можно также предпринять любые другие действия по инициализации, такие как подключение к базе данных и открытие файла. У класса может быть несколько конструкторов экземпляра, которые могут быть перегружены (т.е. иметь разные типы параметров). Подобно статическим конструкторам, конструкторы экземпляра именуются по названию определяющего их класса. Одна заслуживающая упоминания особенность конструкторов экземпляра состоит в необязательности выражения инициализатора. С помощью инициализатора, который следует за двоеточием после списка параметров, можно вызвать конструктор базового класса или другой конструктор того же класса, указывая, соответственно, ключевые слова base и this. Рассмотрим следующий пример кода с двумя комментариями:

class Base

{

public int x = InitX();

public Base ( int x )

{

this.x = x; // устранение неоднозначности между

// параметром и переменной экземпляра

}

}

class Derived: Base

{

public Derived( int a )

: base( a ) // вызов конструктора базового класса

{

}

}

Метод определяет процедуру, которую можно выполнить над объектом или классом. Если метод является методом экземпляра, то его можно вызывать на объекте. Если же метод статический, его можно вызывать только на классе. Отличие между ними в том, что метод экземпляра имеет доступ и к полям экземпляра объекта, и к статическим полям класса, в то время как статический метод не имеет доступа к полям и методам экземпляра. Статические методы могут иметь доступ только к статическим членам класса.

В следующем примере демонстрируется применение метода экземпляра:

public class A

{

private void SomeOperation()

{

x = 1;

this.у = 2;

z = 3;

// присваивание this в объектах является ошибкой

// A newinstance = new A();

// this = newinstance;

}

private int x;

private int y;

private static int z;

static void Main()

{

A obj = new A ();

obj.SomeOperation();

System.Console.WriteLine ( " x = {0}, y= {1}, z= {2}",

obj. x, obj.y, A.z );

}

}

В методе Main создается новый экземпляр класса А и затем вызывается метод SomeOperation через экземпляр этого класса. Внутри тела метода SomeOperation имеется доступ к полям экземпляра и статическим полям класса, поэтому присваивать им значения можно просто с использованием их идентификаторов. Несмотря на то что, как уже упоминалось, в методе SomeOperation можно присваивать значение статическому члену z, не квалифицируя его, для достижения лучшей читабельности кода во время присваивания значений следует квалифицировать статические поля даже в методах, относящихся к тому же самому классу. Поступив так, вы поможете тем, кому придется сопровождать код, даже если это будете вы сами!

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

 


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-05-03; Просмотров: 966; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.123 с.)
Главная | Случайная страница | Обратная связь