Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_answers (1).docx
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
2.9 Mб
Скачать

Import Network;

TYPE SocketStat = POINTER TO RECORD

CurrentSpeed : INTEGER;

END;

PROCEDURE Network Send+ (S: Network.Socket; Data: String);

Var Stat: SocketStat;

BEGIN

BASE(S, Data);

Stat := FindSocketStat(S);

Stat.CurrentSpeed := … ;

END;

END NetworkMonitor;

TYPE SocketStat = POINTER TO RECORD (Socket)

END;

PROCEDURE … (S:Network.Socket; Data: String);

BEGIN

BASE(S,Data);

S(SocketStat).CurrentSpeed := … ; END;

В модуле Network создаются экземпляры типа Socket, а не SocketStat. Чтобы создать экземпляр нового типа, нужно использовать паттерны (Factory Pattern).

Если расширяющих модулей несколько и все они создают свои типы на основе типа Socket, то Factory Method не работает.

Ассоциирование данных

MODULE NetworkMonitor;

Var SocketStatCollection: … ;

PROCEDURE Network Send+ (S, Data)

BEGIN

BASE(S, DATA);

FindSocketStat(S).CurrentSpeed; END;

Минусы: высокая трудоемкость (поддерживать ассоциацию при create и удалении первичных объектов). При уничтожении socket нужно уничтожить socketstat. => снижение надежности, уменьшение производительности, необходимость синхронизации – при доступе к таблицам поиска необходимо синхронизировать потоки;

РЕШЕНИЕ: дополнение типов данных.

MODULE NetworkMonitor;

IMPORT Network;

TYPE

SocketStat = POINTER TO RECORD EXTENSION Network.Socket

END;

PROCEDURE NetworkSend+ (…)

BEGIN BASE(S,Data);

S[SocketStat].CurrentSpeed;

END;

Реализация: 1) дополнение расширяет все экземпляры определенного типа данных;

2) память под дополнение выделяются на лету при первом обращении к объекту;

3) дополнения можно использовать только для данных, которые находятся в динамической памяти;

4) ортогональны «наследованию», могут создаваться в любом количестве и для любого типа данных в иерархии;

Эти методы позволяют:

1) упростить создание, сопровождение и развитие расширяемых систем;

2) повысить надежность;

3) повысить производительность;

4) обеспечить статическую и динамическую верификацию правил расширения системы.

АОП – принципы программирования. АОП зависит от ООП.

Oberon

Oberon: разрабатывался для создания маленьких систем управления, ОС. Oberon System для Lilith PC.

Понятие объекта не вводится, но свой язык Вирт называет ООП-языком.

Главное свойство – свойство расширяемости (наследование типов, но виртуальные методы отсутствуют. Есть записи и их наследование. )

Boolean, char, integer, real, longint, set ( 0..31)

ArrayType = ARRAY length(“,” length) of type – нужно явно указывать длину массива, динамических массивов нет. Нумерация массивов с 0.

RecordType = RECORD [“(BaseType)”] [Fields] end;

PointerType = POINTER to type; Только для записей и массивов. Нельзя преобразовать указатель в число, но есть функция addr, возвращающая int. Число нельзя интерпретировать как указатель.

Система Oberon имеет сборщик мусора.

Процедурная переменная – указатель на функцию.

ProcedureType = PROCEDURE[formal parameters].

=” | “#” (не равно) | “<” | “<=” | “>” | “>=” | “IN” | “IS”

+” “-” “OR”

*” “/” “DIV” “MOD” “&”

If (ch>=”A”) then

elsif (ch>=”0”) then

elsif (ch=22x) then ReadStr.

End.

CASE k of

0:

|1:

|2:

End.

Компилятор оптимизирует, делая goto по таблице, где индексы – ключ k.

Oberon минималистичен – из него нельзя ничего выбросить.

WHILE j>0 DO

j:=j DIV 2;

i:=i+1;

END;

While m>n DO m:=m – n

Elsif n>m DO n:=n-m

REPEAT

UNTIL expression (условие выхода из цикла)

For v:=beg to en BY inc do s end

Beg, en вычисляются только 1 раз.

RETURN м.б. только в конце процедуры, иначе м.б. нарушены принципы структурного программирования.

Существует стандартный модуль System для разработки ОС.

В целях упрощения отказались от обработки исключений.

Component Pascal

ООП + Oberon = Component Pascal

Главная идея уточнений по сравнению с Обероном была в том, чтобы дать проектировщику компонентного каркаса (т.е. интерфейсов модулей, определяющих абстрактные классы для конкретной проблемной области) более полный контроль над ее проектируемыми свойствами в плане безопасности. Введены специальные атрибуты для типов (ABSTRACT, EXTENSIBLE, LIMITED) и методов (ABSTRACT, EMPTY, EXTENSIBLE), позволяя автору программной компоненты (группы модулей) осуществлять контроль в плане того, разрешать или нет модулям-клиентам расширять предлагаемые им типы.

2. Модернизирована несколько устаревшая система основных типов Оберона: теперь набор основных типов Компонентного Паскаля является надмножеством для основных типов языка Java. Основные «рабочие» типы INTEGER, REAL и CHAR соответствуют 32-, 64- (т. н. двойная точность) и 16-(Unicode)-битовым переменным, что позволяет уменьшить разнообразие основных типов, реально используемых в большинстве случаев; использование других типов (LONGINT, SHORTREAL, SHORTCHAR и т. д.) ограничивается специальными приложениями.

3. Добавлены базовые средства для работы с цепочками литер (неявный тип String), что делает Компонентный Паскаль более удобным, чем Паскаль или Оберон, для работы со строками. Цепочки литер представляются массивами литер (ARRAY OF CHAR или ARRAY OF SHORTCHAR), причем значением считается последовательность литер до первого вхождения специальной литеры-ограничителя 0X. Цепочки литер можно сравнивать (подразумевается лексикографическое сравнение) и складывать (конкатенация). Конструкция a := b$ позволяет скопировать в массив литер a цепочку, хранящуюся в массиве литер b (включая литеру-ограничитель 0X), даже если присваивание a := b запрещено (например, из-за разной длины массивов a и b).

13. Имитация модульного программирования в языке C++. Понятие пространства имен.

Имитация модульного программирования в языке C++

В языке С++ очень бедные средства модульного программирования, поэтому для достижения модульности программ, следует придерживаться определенных принципов.

Роль программного интерфейса модуля играет h-файл, а cpp-файл – роль реализации этого модуля.

Внутрь h-файла включаются h-файлы других модулей, необходимые для компиляции интерфейсной части. Внутрь cpp-файла включаются h-файлы других модулей, необходимые для компиляции cpp- и h-файлов интерфейсной части модуля.

Очевидно, что программисту при включении h-файла другого модуля предоставляется выбор: подключить его в h-файле модуля или в cpp-файле. В данном случае предпочтение следует отдавать части реализации модуля (cpp-файл).

При подключении h-файла следует придерживаться следующей схемы: предположим, что наш модуль называется SysModule и состоит из двух частей: SysModule.h и SysModule.cpp.

Рекомендуется следующая схема подключения:

SysModule.h:

#include"Config.h" // наш файл конфигурации подключается первым во всех h-файлах всех наших й проектов

#include"Другой стандартный модуль"

#include"Другой наш модуль"

SysModule.cpp:

#include"Файл предкомпилированных заголовков"

#include"Еще один наш модуль"

#include"Другой стандартный модуль"

#include"SysModule.h" // подключается последним

Поскольку один и тот же h-файл может одновременно включатся в другие h-файлы и несколько раз подключаться при компиляции одного и того же cpp-файла, его следует защищать от повторной компиляции. Для этого в начале любого h-файла вставляются следующие директивы компилятора:

#ifndef __SysModule_h__

#define __SysModule_h__

...

#endif

Таким образом, в том случае, когда файл подключается несколько раз, скомпилируется он только один раз.

Согласно стандарту ISO, любой h- и cpp-файл в С++ должен заканчиваться символом перевода строки.

Предкомпилированные заголовки

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

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

Впрочем, предварительная компиляция заголовка помогает не всегда:

  1. При изменении любого из предкомпилируемых заголовков перекомпилируется весь набор.

  2. При полной перекомпиляции выигрыш по времени получается, когда один и тот же набор применяется как минимум в двух единицах компиляции.

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

Принцип действия предкомпилированных заголовков 

Для управления предкомпилированными заголовками предназначена директива компилятора #pragma hdrstop. Все заголовочные файлы, включенные до этой директивы, помещаются в один образ, например: 

#include <vcl.h> #include <string> #pragma hdrstop

Такая последовательность создаст образ, содержащий скомпилированные vcl.h и string. Этот образ будет использован для другого cpp-файла, если в нем до директивы hdrstop будут включены те же файлы, в том же порядке. Обращу внимание, что важен не только состав, но и порядок следования заголовков – даже если следующий cpp-файл включает те же заголовки, но сначала указан string, а потом vcl.h, то для этого cpp-файла будет создан новый образ. 

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

- ВСЕ cpp-файлы проекта имели одинаковый блок включений до директивы hdrstop;  - в этот блок должны входить ВСЕ стандартные заголовочные файлы, необходимые для проекта.

Понятие пространства имен

В больших проектах наблюдается серьезная проблема – конфликт идентификаторов. Она решается с помощью пространства имен.

namespace Sys

{

int var;

void Proc();

}

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

var = 10;

Proc();

за пределами надо использовать полную форму записи:

Sys::var = 10;

Sys::Proc();

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

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

using namespace Sys; (директива)

Но следует отметить, что данная конструкция является причиной многих ошибок, поэтому так писать не стоит.

Существует второй способ открыть пространство имен – это открыть его для конкретного определения:

using Sys::Proc(); (определение)

Но рекомендуется использовать Sys::Proc();

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

::Funk();

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

namespace

{

...

}

Пространства имен могут быть вложенными:

namespace Sys

{

namespace Local

{

int var;

...

}

...

}

Sys::Local::var = 10;

Замечание! Когда возникает желание объявить переменный тип данных или подпрограмму внутри пространства имен, а реализовать за пределами (или наоборот), следует поступать так:

Классы, функции и переменные, которые являются стандартными компонентами компиляторов С++ , находятся в пространстве имен std. Это относится к заголовочным файлам без .h.

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

• Можно поместить

using namespace std ;

перед определением функции в файле, в результате чего все содержимое пространства имен std будет доступно для каждой функции в файле.

• Можно поместить

using namespace std; в определении функции

using std::cout;

std ::cout.

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

Пространства имен открыты. Это означает, что можно включать новые имена в существующие пространства имен.

Предположим, что одно и то же имя определено как в пространстве имен, так и области объявлений. При попытке применить объявление using, чтобы поместить имя из пространства

имен в область объявлений, оба имени вступят в конфликт, и отобразится сообщение об ошибке. Если с помощью директивы using перенести имя из пространства имен в область

объявлений, локальная версия этого имени перекроет версию из простраства имен.

Недопустимо использование имен из неименованного пространства в любом файле, кроме того,

что содержит объявление пространства имен. Это может служить альтернативой применению статических переменных с внутренним связыванием.

14. Классы в языке C++. Наследование. Конструкторы и деструкторы. Стандартные конструкторы. Создание объектов по значению (на стеке) и по ссылке (в динамической памяти). Операторы new и delete. Размещающий оператор new. Порядок конструирования и разрушения объектов. Вложенные определения классов. «Друзья» класса. Статические члены класса.

Спецификация базового типа выполняет три вещи:

• Определяет, сколько памяти нужно объекту.

• Определяет, как интерпретируются биты памяти.

• Определяет, какие операции, или методы, могут быть применены с использованием этого объекта данных.

Классы в С++

Классы в С++ определяются с помощью одного из ключевых слов: class или struct.

Атрибуты доступа в классах: public, protected, private. Их можно чередовать.

В работе секций protected и private в Delphi и C++ есть различия:

ƒ В Delphi классы внутри одного модуля могут обращаться к данным и подпрограммам друг друга без ограничений. А действие секций protected и private распространяется только за пределами данного модуля. В С++ действие этих секций распространяется на любые два класса. Но установленные ограничения можно обойти с помощью специального оператора friend:

Class TTextReader

{

friend class TList;

};

После этого объект класса TList может обращаться к полям из секций private и protected класса TTextReader.

Метод класса может быть реализован по месту или отдельно от класса:

class TTextReader

{

public:

TTextReader();

~TTextReader() { ... } // по месту

};

TTextReader::TTextReader() // отдельно от класса – квалифицированное имя

{

...

}

Если класс описан в интерфейсной части модуля, его методы рекомендуется реализовывать отдельно от класса в cpp-файле, иначе при компиляции получаются огромные объектные модули. В том случае, когда некоторый класс надо сделать inline-методом, следует писать так:

class TTextReader

{

public:

TTextReader();

~TTextReader();

int ItemCount();

};

inline int TTextReader::ItemCount()

{

...

}

Любая функция внутри определения класса автоматически становится встроенной.

При обращении к inline-методу идет обращение к полю. Метод д.б. перенесен в h-файл. Иначе компилятор будет делать вызов метода. На этапе компоновки получим ошибку линкера «нет метода».

В С++ объекты могут агрегироваться по ссылке и по значению (агрегирование по ссылке похоже на агрегирование в Delphi).

Агрегирование по значению:

class TDelimitedReader

{

public:

...

private:

std::string m_FileName;

};

Агрегированные по значению объекты конструируются автоматически в порядке объявления после вызова конструктора базового класса (если не указан другой способ инициализации).

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

скобки конструктора:

TTextReader::TDelimitedReader() : TTextReader(), m_FileName("c:/myfile.txt")

{

...

}

Следует отметить, что данная запись отличается от следующей записи:

TDelimitedReader::TDelimitedReader() : TTextReader()

{

m_FileName = "c:/myfile.txt";

}

Во втором случае строка вначале создается пустой, а в теле конструктора переприсваивается.

Объекты, агрегированные по ссылке, нужно создавать вручную с помощью оператора new, а удалять – с помощью оператора delete:

class TDelimitedReader : publicTTextReader

{

...

private:

std::string m_FileName;

TItems *m_Items;

};

TDelimitedReader::TDelimitedReader() : TTextReader(), m_FileName("c:/myfile.txt")

{

m_Items = new TItems;

}

TDelimitedReader::~TDelimitedReader()

{

delete m_Items;

}

Правило конструирования агрегированных объектов:

ƒ объекты, агрегированные по значению и константные ссылки инициализируются до тела конструктора.

ƒ объекты, агрегированные по ссылке, инициализируются в теле конструктора.

Агрегация – размещение данных в виде полей внутри других данных.

Наследование

Наследование класса выполняется следующим образом:

сlass TDelimitedReader: public TTextReader

{

...

};

При наследовании указываются атрибуты доступа к элементам базового класса (public, protected, private). Для того чтобы понять смысл атрибута доступа к базовому классу, базовый класс следует рассматривать, как поле производного класса.

Private-наследование: в производном классе не видно то, что в базовом классе объявлено как private.

Protected-наследование: в производном классе видим public & protected.

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