Дополнительная спецификация команд виртуальной машины для языка ООП (t – логическое значение) — КиберПедия 

Двойное оплодотворение у цветковых растений: Оплодотворение - это процесс слияния мужской и женской половых клеток с образованием зиготы...

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...

Дополнительная спецификация команд виртуальной машины для языка ООП (t – логическое значение)

2021-01-31 74
Дополнительная спецификация команд виртуальной машины для языка ООП (t – логическое значение) 0.00 из 5.00 0 оценок
Заказать работу

Исходной состояние Результат
s e (NOP. c) d m s e c d m
s e (RETURN. c) d m s e c d m
(f v. s) e (PUT i. c) d m s e c d (m | m[f,i] = v)
(f. s) e (GET i. c) d m (m[f,i]. s) e c d m
((a1 a2... aK) f.s) e (INVOKE sig. c) d m s ((a1 a2... aK). e) (f[sig]. c) d m
(Obj. s) e (CHECK type. c) d m (t. s) e c d m

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

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


 

 

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

 

7.3. Особенности С++

 
С концепцией ООП связано представление о возможности сокрытия ин- формации, наследования определений по иерархии классов и полиморфизма реализации операций и функций. Переход к ООП в языке С++ привел к пере- смотру некоторых решений на уровне языка и компилятора. Рассмотрим осо- бенности C++ как наиболее популярного языка ООП, в котором достижима схема, обобщающая комплекс решений задачи в виде ацикличного графа с возможными горизонтальными связями. Такие решения направлены на программирование ряда версий решения задачи без отмены ранее отлажен- ных решений, но с формированием новых областей видимости, в которых устаревшая часть программы может быть просто оттеснена. Важно принять во внимание следующее:

– компилируемая программа на С++ представляет собой иерархию обла- стей видимости определений элементов классов, доступ к которым регламен- тирован;

– компиляция методов, конструкторов, функций и перегруженных опера- ций управляется форматом списка фактических параметров, что привело к более жестким правилам объявления типов данных и ограничивает свободу конкретизации списка параметров при вызове функций;

– возникают рекомендательные средства повышать эффективность коди- рования вызовов функций указанием на inline-включение;

 
появляется уровень программирования шаблонов для представления об- щих схем обработки контейнерных типов разнотипных данных.

 

Программа Пояснение
//HELLO.CPP Комментарий с именем файла программы
#include <iostream.h> Препроцессор с вызовом библиотеки
void main () Головная функция
{ cont << ”\nHello, World!\n”; Вывод приветствия на стандартное устройство
}  

Пример 54. Программа на языке С++

Практический выигрыш от ООП можно сразу показать на технике при- менения средств вывода данных.


 

Фрагмент Пояснение
printf (“x = %d, y = % s”, x, y); Вывод целого и строки. «x» должен быть целым, а «y» – строкой

 
Пример 55. Язык Си: Вывод по библиотеке функций stdio.h

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

Фрагмент Пояснение
cout << “x = ” << x << “, y = ” << y; Поток вывода управляется фактиче- ским типом данных

Пример 56. Язык С++: Вывод по библиотеке классов iostream.h

Программисту достаточно перечислить элементы вывода в естественном порядке. Библиотека классов iostream.h содержит перегрузку операции «<<» для всех основных типов данных, что позволяет компилятору выбрать нуж- ный шаблон кода программы, и программист освобожден от необходимости представлять в программе сведения о формате выводимых данных.

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

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

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

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


 

 

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

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

 

Фрагмент Пояснение
class complex { double re, im; public: complex(double r, double i) { re=r; im=i; } friend complex operator+(complex, complex); friend complex operator*(complex, complex); }; Объявление класса объек- тов с закрытыми полями и общедоступными конструкторами и перегрузкой операций

Пример 57. Перегрузка арифметических операций для их использования в выражениях над комплексными числами25

Члены класса создаются в порядке их описания, а уничтожаются они в обратном порядке. Член класса может быть частным (private), защищенным (protected) или общим (public).

 

Фрагмент Пояснение

void f()

{

complex a = complex(1,3.1); complex b = complex(1.2,2); complex c = b;

Объявление функции, выполняющей процедуру,
конструирования 3-х комплексных чисел и их инициирования,
a = b+c;

 

и обработка чисел с помощью пере- груженных операций

b = b+c*a;
c = a*b+complex(1,2);
}

 
Пример 58. Интерпретация этих операций задана определениями функций с именами operator+ и operator*26

 

25 Страуструп Б. Язык программирования C++ для профессионалов [Электронный ресурс]. Режим доступа: http://www.intuit.ru/department/pl/cpp2/.

26 Там же.


 

 

Если b и c имеют тип complex, то b + c означает (по определению) opera- tor + (b, c). Сохраняются обычные приоритеты операций, поэтому второе вы- ражение выполняется как b = b + (c*a), а не как b = (b + c)*a. При перегрузке операций нельзя изменить их приоритеты, равно как и синтаксические пра- вила для выражений.

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

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

 
Одним из самых полезных видов классов является контейнерный класс, т. е. такой класс, который хранит объекты каких-то других типов. Списки, массивы, ассоциативные массивы и множества – все это контейнерные классы.

 

Фрагмент Пояснение
template<class T> Объявление параметризованного шаблона
class stack { T* v; T* p; int sz; для создания классов стеков в зависимости от задаваемого типа хранимых элементов при известном объеме стека.
public: Stack (int s) { v = p = new T[sz=s]; } ~stack () { delete[] v; } Общедоступны: конструкторы стека и деструкторы стека,
void push(T a) { *p++ = a; } T pop() { return *--p; } функции работы со стеком
int size() const { return p-v; } };  

Пример 59. Шаблон типа для класса.

Стек, содержащий элементы произвольного типа27

Префикс template<class T> указывает, что описывается шаблон типа с па- раметром T, обозначающим тип, и что это обозначение будет использоваться

 

27 Страуструп Б. Язык программирования C++ для профессионалов [Электронный ресурс]. Режим доступа: http://www.intuit.ru/department/pl/cpp2/.


 

 

в последующем описании. После того, как идентификатор T указан в пре- фиксе, его можно использовать как любое другое имя типа.

 
Область видимости T продолжается до конца описания, начавшегося пре- фиксом template<class T>.

Имя шаблонного класса, за которым следует тип, заключенный в угловые скобки <>, является именем класса (определяемым шаблоном типа), и его можно использовать как все имена класса.

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

Функции в шаблоне типа могут и не быть подстановками.

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

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

При организации наследования в отличие от обобщенных функций рабо- тает модель обмена сообщениями:

– объекты обладают свойствами;

– посылают сообщения;

– наследуют свойства и методы от предков.

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


 

 

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

 

Функциональные модели ООП

Чтобы сравнить дистанцию ООП с ФП П. Грем (Paul Graham) в описании стандарта языка Common Lisp предлагает рассмотреть модель встроенного в Lisp объектно-ориентированного языка (ОО-язык), обеспечивающего ос- новы ООП [23]. Встраивание ОО-языка показывает силу ФП при его харак- терном применении – моделировании разных стилей программирования (начиная со стандартного программирования в виде prog-формы, предложен- ной Дж. Маккарти). В языке Lisp, точнее в СП для поддержки языка Lisp, есть разные способы размещать коллекции свойств. Один из них – представлять объекты как хэш-таблицы и размещать свойства как входы в нее. П. Грем приводит пример (8 строк) реализации ООП на базе хэш-таблиц. Фактически наследование обеспечивает единственное средство реализации языка Lisp: все это работает благодаря реализации рекурсивной версии GETHASH, рас- ширяющей ядро языка. Впрочем, Lisp по своей природе изначально был ОО-языком. Определение методов может достичь предельной гибкости бла- годаря возможности генерировать определения функциональных объектов с помощью DEFMACRO или функцией категории FSUBR.

 
Реализация ООП с помощью хэш-таблиц обладает слегка парадоксальной окраской: гибкость у нее больше, чем надо и за большую цену, чем можно позволить. Уравновесить это может подобная реализация на базе простых векторов. Этот переход показывает, как функциональное программирование дает новое качество «на лету». В опорной реализации фактически не было реализационного разделения объектов на экземпляры и классы. Экземпляр – это просто класс с одним-единственным предком. При переходе к векторной реализации разделение на классы и экземпляры становится реальным, также становится невозможным превращать экземпляры в классы простым измене- нием свойства.

Более прозрачная модель ООП на базе обычных списков, иллюстрирую- щая глубинное родство ФП и ООП, реализована в системе CLOS. Показан- ный ниже пример 60 работает по первому аргументу (выбор подходящего ме- тода рассчитан на то, что достаточно разобраться с одним аргументом), CLOS делает это на всех аргументах, причем с рядом вспомогательных средств, обеспечивающих гибкий перебор методов и анализ классов объектов. Рас- смотрим примеры использования классов и экземпляров объектов:

(defclass ob () (f1 f2 …)); объявление класса


 

 

 
Это означает, что каждое вхождение объекта будет иметь поля-слоты f1 f2... (Слот – это поле записи или списка свойств.) Чтобы сделать представи- теля класса, мы вызываем общую функцию создания свойств атома SETF:

(SETF с (make-instance 'ob)); создание экземпляра объекта

Чтобы задать значение поля, используем специальную функцию slot- value, записывающую значение объекта:

(SETF (slot-value c) 1223)

До этого значения полей объекта были не определены.

Простейшее определение слота – это его имя. Но в общем случае слот может содержать список свойств. Внешне свойства слота специфицируются как ключевые параметры функции. Это позволяет задавать начальные значе- ния. Можно объявить слот совместно используемым:

:allocation:class

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

Рассмотрим технику ООП на примере организации Lisp-вычислений – популярный пробный камень демонстрации возможностей новых ЯП.

 

Определение Пояснение
(defclass expr () ((type:accessor td) (sd:accessor ft)) (:documentation "C-expression")) Суперкласс для всех структур Lisp-выражений. Заданы ключи доступа к общим полям объектов: тип и операнд
(defclass un (expr) ((type:accessor td) (sd:accessor ft)) (:documentation "quote car *other *adr")) Класс для унарных форм. Доступ можно унаследо- вать от суперкласса, а здесь не дублировать
(defclass bin (expr) ((type:accessor td) (sd:accessor ft) (sdd:accessor sd)) (:documentation "cons + lambda let")) Класс бинарных форм.   Третье поле для второго операнда

 
Пример 60 (начало). ОО-определение Lisp-компилятора


 
 

 

 


Определение Пояснение
(defclass trio (expr) ((type:accessor td) (sd:accessor ft) (sdd:accessor sd) (sddd:accessor td)) (:documentation "if label")) Если взять суперкласс (bin), то можно не объяв- лять первые 3 поля.   Четвертое поля для треть- его операнда
(defmethod texrp ((x expr) (nt atom)) (SETF (slot-value x 'type) nt) (SETF (td x) nt);;--;; variant (:documentation "объявляем тип выражения")) Метод представления вы- ражений для компиляции
(defmethod spread ((hd (eql 'QUOTE)) (tl expr)) (let ((x (make-instance 'un))) (SETF (ft x) (car tl)) (SETF (td x) hd) ) (:documentation "распаковка выражения")) Метод разбора констант и выражений с унарными операциями
(defmethod compl ((hd (eql 'QUOTE)) (tl expr)) (list 'LDC tl) (:documentation "сборка кода")) Метод компиляции констант
(defmethod compl ((hd (eql 'CAR)) (tl expr) N) (append (compl(ft tl) N) '(CAR)) (:documentation "сборка кода")) Метод компиляции выражений с унарными операциями
(defmethod spread ((hd (eql 'CONS)) (tl expr)) (let ((x (make-instance 'bin))) (SETF (ft x) (CAR tl)) (SETF (sd x) (cadr tl)) (SETF (td x) hd) ) (:documentation "распаковка выражения")) Метод разбора выраже- ний с бинарными опера- циями
(defmethod compl ((hd (eql 'CONS)) (tl bin) N   ) (append (compl(sd tl) N) (compl(ft tl) N) '(CONS)) (:documentation "сборка кода")) Метод компиляции выра- жений с бинарными опе- рациями с прямым поряд- ком компиляции операн- дов
(defmethod compl ((hd (eql '+)) (tl bin) N) (append (compl(ft tl) N) (compl(sd tl) N) '(ADD)) (:documentation "сборка кода")) Метод компиляции выра- жений с бинарными опе- рациями с обратным по- рядком компиляции опе- рандов

 
Пример 60 (продолжение). ОО-определение Lisp-компилятора


 
 

 

 


Определение Пояснение
(defmethod spread ((hd (eql 'IF)) (tl expr)) (let ((x (make-instance 'trio))) (SETF (ft x) (CAR tl)) (SETF (sd x) (cadr tl)) (SETF (td x) (caddr tl)) (SETF (td x) hd) ) (:documentation "распаковка выражения")) Метод разбора выраже- ний с триадными опера- циями
(defmethod compl ((hd (eql 'IF)) (tl expr) N) (let ((then (list (compl(sd tl)N) '(JOIN))) (else (list (compl(td tl)N) '(JOIN)))) (append (compl(ft tl)N) (list 'SEL then else) ) )(:documentation "сборка кода")) Метод компиляции выра- жений с триадными опе- рациями
(defmethod parh ((x expt)) (let (ftx (ft x)) (COND ((ATOM ftx) (spread 'ADR ftx)) ((member (CAR ftx) '(QUOTE CAR CONS + IF LAMBDA LABEL LET)) (spread (CAR ftx) (CDR ftx)) (T (spread 'OTHER ftx))) )(:documentation "шаг разбора")) Метод разбора произволь- ных выражений

Пример 60 (окончание). ОО-определение Lisp-компилятора

 

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

Более интересный вопрос, что же еще может дать функциональный стиль и традиция реализации функциональных систем программирования?

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


 
 

 


Другая модель ООП, полученная на базе обычных списков свойств (атри- бутов), также иллюстрирует глубинное родство ФП и ООП [10]. Нужно лишь уточнить определение Lisp-интерпретатора, чтобы методы рассматривались как особая категория функций, обрабатываемая специальным образом.

 

Спецификация

Таблица 36

 
Парадигматическая характеристика парадигмы ООП

Параметр Конкретика
Эксплуатаци- онная праг- матика ЯП Практичное программирование, нацеленное на разумный компро- мисс в пространстве противоречивых критериев с приоритетом критериям сферы приложения программ
Регистры аб- страктной машины S E C D M S – стек промежуточных результатов. E – стек локальных переменных. C – текущая программа. D – дамп для восстановления контекста при рекурсии. M – общая память хранимых объектов. Результат рассредоточен по именованным состояниям памяти
Категории команд абстрактной машины Загрузка в стек. Сохранение значений. Манипулирование стеком. Арифметические и логические операции. Передачи управления. Выбор определения метода, функции, операции. Вызов метода. Возврат из метода. Обработка исключений. Работа под монитором (параллелизм)
Реализаци- онная праг- мати Сочетание статического представления методов с динамикой раз- мещения объектов, включая автоматизацию повторного использо- вания памяти. Представление сигнатуры для динамического выбора конкретного метода обработки объект
Парадиг- мальная специфика Процесс программирования сводится к последовательности расши- ряемых по мере целесообразности программ. Использование предметной типизации классов объектов как веду- щего параметра выбора конкретной техники обработки данных

ЯЗЫКИ СВЕРХВЫСОКОГО УРОВНЯ

 
Подготовка программ на базе языков сверхвысокого уровня (ЯСВУ) наце- лена на длительный срок жизни запрограммированных решений особо важ- ных и сложных задач. Удлинение жизненного цикла достигается представле- нием обобщенных решений с определенной степенью свободы по отноше- нию к полным пространствам допустимых смежных компонент, реализованных ранее или планируемых на будущее характерно для ЯСВУ. Реализационное сужение семейства процессов, допускаемых семантикой ЯВУ, противоречит целям ЯСВУ или его концепциям по следующим праг- матическим мотивам:

– высокий уровень абстрагирования программируемых решений;

– решаются задачи, зависящие от непредсказуемых внешних факторов;

– базовые средства и/или алгоритмы вычислений используют паралле- лизм;

– актуальны прагматические требования к темпу и производительности вычислений;

– эксплуатируются динамически реконфигурируемые многопроцессор- ные комплексы.

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

 
Для ЯСВУ характерно применение регулярных, математически ясных и корректных, абстрактных структур, при обработке которых возможны пре- образования данных и программ, использование подобий и доказательных построений. Все это призвано гарантировать не только высокую производи- тельность вычислений, но и надежность процесса разработки программ и длительность их жизненного цикла. Типичны алгебраические спецификации, теоретико-множественные построения, параллелизм, модели процессов раз- работки программ. Изобретаются специальные системные средства, повы- шающие емкость представлений, их общность и масштабируемость. Есте- ственный резерв производительности компьютеров – параллельные про- цессы. Их организация требует контроля и детального учета временных от- ношений и неимперативного стиля управления действиями. Суперкомпью- теры, поддерживающие высокопроизводительные вычисления, нуждаются в особой технике системного программирования, которая еще не сложилась, хотя уже имеется опыт эффективного решения особо важных задач.


 

 

Наиболее очевидно, что можно как бы «просачивать» определения функ- ций над простыми данными, распределять их по структурам данных и тем самым распространять простые функции на сложные данные подобно мат- ричной арифметике. Похожие построения предложены в языке APL, ориен- тированном на обработку матриц, и Бэкусом в его программной статье о функциональном стиле программирования, проиллюстрированном на языке FP.

 

 

 

 


 

 


Поделиться с друзьями:

Археология об основании Рима: Новые раскопки проясняют и такой острый дискуссионный вопрос, как дата самого возникновения Рима...

Автоматическое растормаживание колес: Тормозные устройства колес предназначены для уменьше­ния длины пробега и улучшения маневрирования ВС при...

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...

Кормораздатчик мобильный электрифицированный: схема и процесс работы устройства...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.106 с.