лекции (2008) (by Михайлишин Алексей_ Жбанков Денис_ Щербинин Виктор_ Чеботарев Павел) (1160831), страница 11
Текст из файла (страница 11)
C# и Java у каждогокласса есть функция Equals (ну или equals), они возвращают bool. По умолчанию сравниваются ссылки (this== 0). Плюс к этому существует статическая функция от двух параметров, на случай сравнения null с null(чтобы не было разыменования null). Существует интерфейс IComparable, не будем на нем подробноостанавливаться.[lect27]IV) Перегрузка операций. Неявные преобразования (частный случай перегрузки)В языке Оберон есть понятие "расширяющие преобразования"int i;double d;i=d; // идет преобразование с потерей информации. ~i=(int)dНеявные преобразования есть почти во всех языках, за исключением языка Ада. Архитектор Ада считал, чтонеявные преобразования это всегда плохо. Как правило, все современные языки разрешают неявноепреобразование между простыми типами.Вопрос: разрешать ли пользователю неявные преобразования? Когда Страуструп проектировал свой С++,в то время была идея на запрет неявных преобразований. Но эта идея оказалась непрактичной, и все жебыли разрешены пользовательские неявные преобразования.C++, C# - допускают пользовательские неявные преобразования и можно перегружать стандартныеоперации.
Delphi, Java - запрещают. Перегрузка стандартных операций запрещена.Аргументы против преобразования:• любое неявное преобразование может вести к потере информации и => ненадежности.• семантика н.п. может не совпадать с тем, что ожидает программист.Классы, которые реализовал Страуструп в своем языке:• Файлы ввода/вывода. Эти классы можно назвать удачными (но, тем не менее, многие до сих порпользуются функциями типа printf)• Complex - класс комплексных чисел.Пример на фортране:A=B*CEXP(-k*i)/D // например B вещ., i - мнимая единица,D - комплексное, k целая. Все работает.Чтобы такое реализовать на Си:Complex Plus(Complex c1, Complex c2); //...Mult(B, Div(CEXP(-Mult(k,i)), i)))// ...На С++:Complex operator +(Complex c1, Complex c2);Если бы не было неявного преобразования, пришлось бы писать все эти операторы плюса для double, float,int, short, char, ...
Т.е. в этом случае пришлось бы реализовать несколько сотен функций, суть которыхфактически сводится к преобразованию между типами и выполнению уже запрограммированной операции.XX(T) // конструктор преобразования.Теперь достаточно написать Complex(double), потому что к double неявно преобразуется все остальное.Другой пример:string( const char *)s = "string"; // компилятор вставит s = string("string");Дополнительно в С++ появился оператор преобразования.operator T() // нестатическая функция-член, определяет преобразование X => TПример:operator const char *() constНо тут же появилось много проблем.
Например, проблема с вектором:class Vector {int *body;int size;public:Vector(int sz) { body = new int[size=sz];}Vector() {...}}Иногда такой конструктор случайно оказывается конструктором преобразования.Vector v(20); // Все ок.V = 3; // Компилятор вставит V = V(3), т.е. создаст вектор длины 3. Но по сутинельзя приводить int к Vector.Позже эту проблему решили так: появилось ключевое слово explicit, которое говорило о том, чтоконструктор можно вызывать только явно. Для совместимости с прошлым кодом работал конструкторпреобразования.В С# учли этот опыт и ввели слова explicit и implicit.
По умолчанию подразумевается explicit.Явное преобразование: T ConvertToT() // X=>TT t;X x;t = x; // ~ t = x.operator T()Оператор дает возможность преобразования из базового класса в производный класс.Создатели Delphi и Java отказались от неявных преобразований вообще. В C# любые операции должныбыть статическими функциями класса.class X {...}class Y {...}public implicit operator Y(X x){...} // эту функцию можно было сделать членомкласса YЕсли перед функцией стоит implicit, то такая операция может вызываться неявно.умолчанию) - только явно.Если explicit (поJava:S1+S2string S;s+i; //~ s+i.toString(); Таким образом, неявные преобразования в Java все же есть,но они встроены в компилятор.Systemjava.lang // типы данных, которые встроены в язык.В поздних версиях С++ компилятор также начинает что-то знать о стандартной библиотеке, например привозбуждении исключения он вызовет std::exceptionВ C# нельзя делать перекрытие () и [], потому что у программистов могут быть разные представления осемантике таких операций.
Вместо этого в C# есть специальная функция - индексатор.int this[string s] {...} // аргумент в квадратных скобках.Теперь можно делать что-то вроде этого:int <- x["string"]V) Свойства (property)Свойство при реализации выглядит как член данных, а при реализации как 2 функции - установка значенияи взятие значения. (get и set). Понятие свойства отражает дуализм данных и операций.C#, Delphi - поддерживают свойства.C++, Java не поддерживают свойства.Функции доступа (accessors) - getter, setter.Delphi:type X = class....property p:integer read accessor write accessor;// где accessor - либо имя переменной члена-класса,функции.// для read - имя функции без параметров// для write - имя процедуры с одним значеннием.либоимяпроцедурыилиПример:type X = classprivateflength:integer;property length:integer read flength write flength;Более сложный пример:type Figure = classprivate a,b: integer;farea: integer;private function GetArea():integer; // в этой функции мы можем вычислить площадьдля a,b, а дальше просто возвращать// уже посчитанное значение fareaproperty width:integer read b write SetWidth;property area:integer read GetArea; // ограничили доступ на запись.private procedure SetWidth(x:integer); // здесь мы можем вычилсить новую площадь.таким образом, мы можем "кэшировать" значения классов для экономии вычислений.Свойства также есть и в C#:class Rect {public:int Width {get{ return _width; }set{ _width = value; _area = _wiidth*_height} // value - ключевое слово,обозначающее устанавливаемое значение.}private int _width, int _height, _area;}В C# 3 было добавлено понятие автореализуемого свойства.class Point {public int x;public int y;public Point(int x, int y) {this.x = x;this.y = y;}}// можно это написать так:class Point {public int x {get;set;}public int y {get;set;} //у этих переменных будет автоматически реализован методset и get}Point p = new Point{x=0; y=0;};...public int x{get; private set;} // доступ к x может быть только внутри блокаинициализации...Глава 7.
раздельная трансляция и разбиение программы на модули.Виды трансляции.1) Цельная трансляция. Программа подается компилятору целиком и выполняется. Для серьезных целейтакое использовать нельзя.2) Пошаговая. Есть понятие конструкций языка. Транслятору дается такая констуркция, он обрабатывает ееи по возможностивыполняет. Пример таких языков: Basic, sh.3) Инкрементная (устарела)4) Раздельная трансляция.4.1) Раздельная незваисимая трансляция.4.2) Раздельная зависимая трансляция.Здесь появляется понятие компиляции, т.е. перевода с одного языка на другой. Появляется понятиеединицы компиляции.4.1: объектный модлуь <= Транслятор <= единица компиляции4.2: транслятор <= Трансляционная библиотека.<= Единица компиляции.Контекст трансляции - набор внешних имен, необходимых для компиляции. (Например, стандартнаябиблиотека).Контекст существует всегда. Если его нет, то он берется из самой единицы компиляции.Фортран, Ассемблер, C/C++ - есть указание контекста трансляции.Пример:[asm]EXTRN X:WORD[/asm][c]extern ...[/c]В языках с зависимой трансляцией подключают контексты.
Все современные языки имеют зависимуютрансляцию.[lect28]Раздельная трансляция:••ЗависимаяНезависимаяВозникает понятие единицы компиляции (ЕК, физический модуль).ЕК > Транслятор > Объектный модульТрансляционная библиотека – таблица имен.Программная библиотека – объектный модуль.Контекст трансляции – набор объявлений внешних имен, необходимых для трансляции.При зависимой трансляции транслятор кроме генерации объектного модуля еще добавляет информацию втрансляционную библиотеку.
Во многих языках логические модули совпадают с физическими. Например, вДельфи в файлах .dcu в первой части – таблица имен, в второй – объектный код.В Обероне, C#, Delphi, Java есть понятие модуля, но нет понятия объектного файла.Для современных ЯП характерно понятие рефлексии – доступа к коду программы во время выполнения.Например, можно считать или записать поле класса по имени этого поля.Так как при зависимой трансляции в модулях фактически записывается текст программы, то для защитыавторский прав существуют средства для т.н. обфускации – запутывания кода модуля.I) Независимая трансляция. С/С++При независимой трансляции для задания контекста трансляции используется директива extern иопределения типов. Необходимо дублировать все внешние имена из одного модуля в другой.
Это ведет ктрудно устранимым ошибкам. Есть технология для избегания таких ошибок. Каждый модуль разбивается назаголовочную часть (.h, .hpp – хедер-файл) и реализационную часть (.c, .cpp). В заголовке описываютсяпрототипы функций, extern – конструкции и объявления типов. Для использования некоторого имени изконтекста трансляции не нужно писать extern-конструкции, а нужно написать #include соответствующегофайла, в котором описано это имя.Проблема в том, что это средство реализовано тривиальными средствами препроцессора.
Отсюда проблема: m.h >m1.h >m2.h, m.h>m2.h (повторное включение, дублирование объявлений типов - ошибка). Поэтому,шаблон хедер-файла следующий:#ifndef __M__H__#define __M__H__//текст объявлений#endif__M__H__ - некоторое уникальное имяБлагодаря такой конструкции содержимое хедер-файла может быть включено только один раз.Так как при использовании больших библиотек некоторые хедеры, имеющие большой объем, включаютсядирективой #include много раз, то для ускорения трансляции современные компиляторы C++ используютпредкомпилированные хедеры.II) Зависимая трансляцияПри зависимой трансляции возникают понятия одностороннего и двустороннего связывания.Одностороннее связывание.Одностороннее связывание: ЕК – клиент запрашивает внешнее имя у ЕК – сервера.В Дельфи, М-2, Обероне ЕК==Лог.