Лекция 11 (Лекции (2009) (Саша Федорова))
Описание файла
Файл "Лекция 11" внутри архива находится в папке "Лекции (2009) (Саша Федорова)". Документ из архива "Лекции (2009) (Саша Федорова)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Онлайн просмотр документа "Лекция 11"
Текст из документа "Лекция 11"
7
Лекция 11.
Глава 8. Определения новых типов и классов
Содержание главы:
-
Понятие члена класса
-
Реализация инкапсуляции
-
Специальные функции
-
Дополнительные вопросы
(Рассматриваем C++, C#, Java, Delphi)
Замечание. Класс – это то, что объединяет структуру данныхи множество операций. НО! В принципе, то же самое умеет делать и модуль. Все функции, имеющие в качестве параметров тип данных, являются функциями над этим типом данных. Принципиальное отличие класса от модуля в том, что класс – это в первую очередь тип данных(однако «модульность» понятия класса часто вылезает).
Пример. Для вычисления cos(x) в классовых языках программирования(чисто объектно-ориентированных языках программирования, где все, что можно описать в прогрмее, есть класс – Java, SmallTalk, C#) приходится «изобретать» класс Math.
В С#, кстати, так и сделали: System.Math.cos(x); Для этого, разумеется, потребовалось ввести в класс понятие статической функции.
Пункт 8.1 О массивах
С точки зрения C++, C#, Java определение класса таково:
class имя [: наследование]{
определение членов класса
}
С этой точки зрения C++ отличается принципом РОРИ(разделение интерфейса и реализации). Требуется обьявить функции-члены внутри класса, а реализованы они могут быть вне самого класса. В C# так нельзя, все функции должны быть реализованы «внутри» класса.
В Delphi ситуация иная:
type T = class (наследование)
объявление членов класса
Реализация процедур и функций обязана быть отдельной (в модуле Implementation), что еще раз подчеркивает инкапсуляцию. Что может быть членом класса? Понятное дело, как и во всех языках, члены-функции и члены-данные. (Кстати, именно наличие членов-функций позволяет говорить о классе как о новом типе данных!)
Член-функция отличается от обычной функции наличием указателя this – ссылки на конкретный объект данного класса, который передается соответствующей функции-члену.
Заметим, что в данной области C#, Java, Delphi с точки зрения основных понятий близки между собой и отличаются от С++. В данных языках имя объекта класса – это ссылка.
Х = new T(); // конструктор! В данном случае – по умолчанию.
Х – это ссылка.
Структура в C# - это класс без референциальной семантики.
C#
struct имя{
определения членов
}
Отличия структуры от класса
-
отсутствие ссылочной семантики
-
память под структуру отводится в зависимости от контекста в стеке или в массиве.
-
Структуру нельзя наследовать, она не может быть предком
-
Операция «точка»
Имя_объекта.имя_члена
Если речь идет о членах-данных, точка работает точно так же, как и в записях. А члену-функции опять-таки передается this.
(Кстати говоря, в Delphi вместо this используется ключевое слово self)
Java, C++
Указатель this часто бывает «полезен»:
-
Класс – некая область видимости, и те имена, которые есть внутри класса, видимы внутри класса (то есть в теле функции-члена) непосредственно. Первый случай, когда нужен this – если имена полей класса скрыты именами формальных параметров. А вот в Delphi имена формальных параметров функции-члена не могут совпадать с именами членов класса, они являются той же областью видимости. Поэтому как правило вместо Name пишут aName)
-
Второй случай когда нужен this – когда его надо кому-то передать. Типичный пример: событие. Объект, пославший событие, передает функции-обработчику указатель на себя.
Локализация имен внутри функций-членов делает код яснее, компактнее и нагляднее.
Наряду с членами-функциями и члеами-данными, внутри класса можно описывать члены-типы.(Пример – икнапсулирование итератора внутри соответствующего контейнера.) Однако вводить классы внутри классов не всегда имеет смысл.
Статические члены, как и все прочие, бывают двух видов:
-
Члены-данные
-
Члены-функции.
В SmallTalk четко введено понятие класса как совокупности экземпляров(instances). У любого класса есть переменные: переменные экземпляра и переменные класса.
Статические члены – это переменные, которые принадлежат классу целиком. И, как следствие, принадлежат каждому экземпляру конкретного класса. А instance variables принадлежат только экземпляру класса. Статические чллены – это отдельно лежащие объекты данных. Это практически тто же самое, что глобальные данные, отличие состоит только в области видимости.
Пример(С++)
class T{
static int x;
static void f();
}
..................
T t;
t.x;
t.f;//обращение затушевывает разницу между статическими и нестатическими членами
или
T::x;
T::f();
Таким образом, для статически членов возможны операции
-
Доступа
-
Разрешения видимости
Более того, статическим члены данные могут существовать даже тогда, когда нет ни одного экземпляра класса.
Видимость статических членов потенциальная и снять ее, в отличие от модулей, нельзя.
В C# и Java статические члены используются чаще, чем в Delphi и C++(в Delphi и C++ есть глобальные функции и глобальные переменные, снижающие потребность в статических членах).. Встречаются и программы без единого экземпляра класса. Тогда используют такую конструкцию:
public class Name{
public static int Main(String[] argc)
Часто бывает в основном для консольных приложений.
Пример. Установление связи между клиентом и сервером. Хорошо бы придумать некий класс, который будет существовать
А) всегда
Б) в единственном экземплляре
Тогда надо запретить произвольное создание экзмемпляра, сделать доступ к контейнеру невозможным извне. То есть – сделать конструктор класса приватным.
class SingleTone{
private: static SingleTone * pInstance;
SingleTone();
SingleTone(const SingleTone );
public: //ну надо же нам как-то обеспечить доступ к классу!
static SingleTone * getInstance()
{
if(pInstance===NULL)
pInstance==new SingleTone();
return pInstance;
}
А кто будет обеспечивать начальное равенство NULL для переменной pInstance?
Очевидно, все статические переменные принадлежат статическому классу памяти.
В С++ существует единственная возможность инициализации статического члена.
Где-то в программе должно быть объявление:
SingleTone * SingleTone::pInstance = NULL;
Размещение статических данных аналогично размещению глобальной переменной.
Итак, объявляем 1 раз – в классе, определяем тоже 1 раз – в коде программы.
SingleTone * SingleTone::pInstance = NULL;
С идеологической точки зрения тут полный бардак – ведь pInstance private! Но иначе нельзя.
Механизм раздельной трансляции в С++ заимствован из Си.
Вывод: без статических переменных и методов не обойтись.
Третий вид статических челнов – вложенные типы данных
Как мы уже выяснили, могут встречаться статические члены-классы.
Пример.
class Ouner{
……………….
class Inner{
……………..
;
};
Вложенный класс – это то же самое, что и обычный класс, только его область видимости – класс Ouner. Если класс Inner будет приватным, то мы вообще никогда его не увидим.
Outer::Inner
Такой класс хорошо бы назвать сататическим – но они все такие!:)
(Все классы статические – они эквивалентны внешним классам, за исключением области видимости и прав доступа)
Недавно появился новый термин: «статический класс» (в язяке C#). В C# статический класс – это особый вид класса, внутри которого есть только статические члены-функции и только статические члены-данные.
Типичный пример: System.Math
Cos(Х) – пример статической функции-члена
pi, e – примеры статических членов-данных.
По определению экземпляры таких кассов ничего в себе не содержат. Класс типа Math, содержащий только статические определения, играет роль модуля. Это просто обычный модуль. С тем отличием, что его можно наследовать и создавать его объекты.
Статический класс – это класс, задача которого – служить контейнером для статических членов и методов. Статическим его назвали для тго, чтобы подчеркнуть модульность.
static class Mool{
public static void f() {............}
public static int i;
}
Замечание. Плата за это – неудобный синтаксис: System. Math. Разработчики Java это заметили и создали статический импорт, при котором все члены статического класса импортируются непосредственно.
Еще дно замечание. В С++ такой возможности как статический класс нет, но она и не нужна – в нем существует ространство имен, где могут быть не только классы, как в C#(там тоже есть namespace, но внутри него могут быть только классы)
Когда мы говорим о статических члленах, все они – члены какого-то класса. Еобходимо, однако, не забывать, что статический класс – это не обязательно член класса. Вот тому пример:
Java:
public class Outer{
public static class Inner{
………………….
};
…………..
};
Только что приведенное объявление вложенного класса на Java строго эквивалентно понятию статического класса, с тем же успехом мы могли перенести его на глобальный уровень.
public static class Inner{
………………….
};
И еще одно замечание о статических классах. В С++ ключевого слова static для класса нет, а в Java есть. Следовательно в Java есть и нестатические классы
В авторском описании Java присутствует пример: Банковская транзакция:
class Счет{
class tranzacton{………..};
};
У класса может быть сколько угодно вложенных кллассов, но у каждого вложенного класса может быть только один «папа».
Как породить экземпляр класса Inner?
В Java все наиболее удобно: понятие инкапсуляции ортогонально и для членов-данных, и для членов-функций, и для членов-классов.
Инкапсуляция во многом важнее наследования: без наследования можно написать хорошую программу, а без инкапсуляции – уже нет.
По оопределению инкапсуляция – некоторая защита, сокрытие свойств объекта. Единицы защиты. Во всех случаях – тип(класс), правила инкапсуляции одинаковы независимо от определения ко всем экземплярам класса.
Управление инкапсуляцией:
-
Управление доступом(С++, C#, Delphi) – видны все инкапсулированные и не инкапсулированные члены.
-
Управление видимостью – есть практически во всех языках программирования
Пример.
class X{
private: void f();
};
class Y: public X{
public: void f(); //все хорошо, приватная f из Х не видна.
}:
А если так:
class X{
public: virtual void f();//(1)
};
class Y: public X{
private: virtual void f(); //(2)можно
};
class Z: public Y{
public:
virtual void f();//В С++ такая ситуация невозможна. Мы не видим функцию f из Y(см (2)) – она приватна. А должны видеть.
А в Java это возможно. f из класса Z будет замещать функцию f из класса Х(см (1))
};