«Работать добросовестно — значит: работать, повышая свою квалификацию, проявляя инициативу в совершенствовании продукции, технологий, организации работ, оказывая не предусмотренную должностными инструкциями помощь другим сотрудникам (включая и руководителей) в общей им всем работе.

OVM/OVM методология/Основы объектно-ориентированного программирования — различия между версиями

Материал из Wiki
Перейти к: навигация, поиск
Строка 93: Строка 93:
  
  
==Virtual Functions and Polymorphism==
+
=== IS-A ===
  
 
IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики.  Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс  является подклассом или более специализированная версия родительского объекта.  Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.
 
IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики.  Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс  является подклассом или более специализированная версия родительского объекта.  Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.
Строка 106: Строка 106:
  
  
[[Файл:38.png]]
+
[[Файл:39.png]]
  
  
  
При представлении двух объектов в компьютерной программе с использованием  
+
При представлении двух объектов в компьютерной программе с использованием наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана базовая модель памяти для IS-А композиции. В этом примере класс  B является производным от А.
наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана  
+
базовая модель памяти для IS-А композиции. В этом примере класс  B является производным от А.
+
  
  
[[Файл:39.png]]
+
[[Файл:40.png]]
  
  
 
SystemVerilog использует ключевое слово  extends на определения наследования между классами:
 
SystemVerilog использует ключевое слово  extends на определения наследования между классами:
  
[[Файл:40.png]]
+
[[Файл:41.png]]
 +
 
 +
 
 +
Класс B получена из A, поэтому он содержит все атрибуты A. Любой экземпляр B не только содержит строку S, но и объект с плавающей запятой F и целое i.
 +
 
 +
== Виртуальные функции и полиморфизм ==
 +
 
 +
 
 +
Одной из причин для создания объектов, используя наследование, является определение  различного поведения для той же операции. Другими словами, поведение, определенное в производном классе переопределяет поведение, определенное в базовом классе. Это можно сделать, используя виртуальные функции. Виртуальная функция – функция, которую можно переопределить в производном классе. Рассмотрим следующий универсальный пакет класса.
 +
 
 +
 
 +
[[Файл:42.png]]
 +
 
 +
[[Файл:43.png]]
 +
 
 +
 
 +
Она имеет три виртуальных функций, чтобы установить содержимое пакета. Различные виды пакетов, требуют различного содержания. Мы используем  generic_packet как базовый класс и получают различные виды пакетов из него.
 +
 
 +
[[Файл:44.png]]
 +
 
 +
Оба packet_A и packet_B могут иметь различные заголовки и трейлеры и  различные форматы полезной нагрузки. Информация  о том, как части пакета  отформатированы, хранится локально внутри производных классов пакета. Виртуальные  функции set_header (), set_trailer (), и set_body () реализованы по-разному в каждом подклассе основываясь на типе пакета. Базовый класс  generic_packet устанавливает организацию классов и типы операций, которые возможны, и производные классы могут изменить поведение  этих операций.
 +
 
 +
Виртуальные функции используются для поддержки полиморфизма: несколько классов, которые могут быть использованы как взаимозаменяемые, каждый с разным поведением. Например, некоторая обработка пакетов не нуждается в информации о том, какой пакет обрабатывается. Единственной необходимой информацией является то, что объект действительно пакет, то есть, он является производным от базового класса. Еще один способ показать, что пакет является предком базового класса - это через  IS-A отношения.  Виртуальные функции являются механизмом, с помощью которого мы можем переопределять
 +
поведение для различных вариантов пакетов.
 +
 
 +
Чтобы посмотреть немного глубже, как работают виртуальные функции, давайте рассмотрим три  класса, связанных друг с другом IS-А отношением.
 +
 
 +
 
 +
[[Файл:45.png]]
 +
 
 +
 
 +
figure является базовым классом; polygon является производным от figure; square происходит от polygon. Каждый класс имеет две функции, draw(), которая является виртуальной, и compute_area (), которая не является виртуальной. Следующий пример показывает,  SystemVerilog код:
 +
 
 +
[[Файл:46.png]]
 +
 
 +
[[Файл:47.png]]
 +
 
 +
Каждая функция выводит его полное имя в виде  class_name :: имя_функции. Мы можем написать простую программу, которая показывает, как виртуальные функции связаны.
 +
 
 +
[[Файл:48.png]]
 +
 
 +
Ниже показано, что происходит, когда мы запускаем эту программу:
 +
 
 +
[[Файл:49.png]]
 +
 
 +
Сначала мы создаем s, квадрат, а затем назначаем его на f и р. базовым классом для square является polygon и базовым классом polygon - figure. Из напечатанного вывода, мы можем заключить, что функции связаны в соответствии со  следующей таблице:
 +
 
 +
[[Файл:50.png]]
 +
 
 +
 
 +
Во всех случаях, compute_area () была связана с конкретным compute_area () функцией с типом ссылки, которая ее определяет -р ссылки  на polygon, таким образом  связаны  polygon :: compute_area (). Так как  compute_area () не является виртуальной функцией. Компилятор может легко определить, какие  версии функций  вызывать, основываясь на типе объекта.
 +
 
 +
Так как draw() является виртуальной, не всегда возможно для компилятора  определить, какую функцию вызывать. Решение принимается на этапе выполнения с помощью
 +
виртуальной таблицы, таблицы привязки функций. Виртуальная таблица используется для привязки  функций, привязка не может быть полностью определена во время компиляции.
 +
 
 +
Обратите внимание, что хотя р представляет собой polygon, вызов p.draw () приводит к  square::draw() вызывается не polygon::draw(), как вы могли бы ожидать.
 +
То же самое происходит и с F-f.draw () связан с кsquare::draw().
 +
объект, который мы первоначально создан представляет собой square,  он поддерживает различные типы операций но, то, что  это square не забывается. Это  работает только потому, что square является производным от polygon, который в свою очередь является производным figure, и потому draw() объявлен как виртуальный. Ошибка компиляции О несовместимости типа возникает при попытке присвоить ей к s
 +
и p  так как с не получено от р.

Версия 17:33, 4 марта 2013

Проект Диплом

Вебинары
Литература

* OVM *

Содержание


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

Процедурные против ООП

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

Каждый язык программирования любой значимости имеет конструкции для создания подпрограмм, процедур и функций, а также синтаксис для передачи в них параметров и возвращаемых значений. Эти функции могут использоваться для создания операций, которые часто используются. Тем не менее, некоторые операции очень распространены (таких как I / O, преобразования данных, численные методы и т. д.). И во избежание переписывания этих операций, программисты посчитали необходимым создать библиотеки часто используемых функций. В результате, большинство языков программирования включает такие библиотеки как часть пакета компилятора. Одним из наиболее известных примеров является библиотека C, которая поступает с каждым компилятором C. Она содержит полезные функции, такие как printf() (), COS (), atof (), и QSort (). Эти функции практически каждый программист будет использовать в тот или иной момент. Представьте себе, что писать собственные подпрограммы ввода / вывода или ваши собственные вычисления для преобразования чисел в строки и строк в числа. Был момент, когда программисты так и делали. Библиотеки универсальных функций все изменили и увеличили общую производительность программирования.

Как показывает практика программного обеспечения и передовых технологий, программисты начали думать на более высоком уровне абстракции, чем инструкции и процедуры. Вместо написания отдельных инструкций, программисты пишут код на языках, которые обеспечивают более высокую модель абстракции компьютера, и компиляторы или интерпретаторы переводят эти модели в конкретные инструкции. Библиотеки, такие как C библиотеки или STL в C + +, являются одной из форм абстракции. Они представляет собой набор функций, которые программисты могут использовать для создания все более сложных программы или абстракции. В своей основополагающей книге Алгоритмы + Структуры данных = Программы, Никлаус Вирт объясняет, что для решения любой задачи программирования, вы должны разработать абстракцию реальности, которая имеет характеристики и свойства задачи, и игнорировать остальные детали. Он утверждает, что набор данных, которые вам нужны для решения проблемы, формирует абстракцию. Поэтому, прежде чем вы сможете решить проблема, в первую очередь необходимо определить, какие данные нужно иметь, чтобы создать решение. Чтобы продолжить создание многократно используемой абстракции, мы должны создавать библиотеки данных объектов, которые могут быть многократно использованы для решения конкретных видов проблем. Поиск способа сделать это приводит к развитию объектно-ориентированных технологий.

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

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

Классы и объекты

Основным модулем программирования на объектно-ориентированных языках, таких как SystemVerilog, является класс. Класс содержит элементы данных, называемые членами, и задач и функций, называемых методами. Для выполнения объектно-ориентированных программ, вы должны создать один или более классов в основной программе, а затем вызвать методы на различных объектах. Хотя термины класс и объект иногда используются как синонимы, как правило, термин класс относится к классу декларации объекта, а термин объект относится к экземпляру класса. Чтобы проиллюстрировать эти понятия, ниже приведен пример простой класс register.


Файл:27.png

Файл:28.png

Это очень простой класс с одной переменной, и двумя методами, read() и write(). Чтобы использовать этот класс, вы создаете объекты путем создания экземпляра класса, а затем вызвать методы объекта, как показано ниже

Файл:29.png

Локальный атрибут contents класса указывает компилятору строго соблюдать границы класса. Если вы пытаетесь получить доступ к contents напрямую, компилятор выдаст ошибку. Вы можете получить доступ к содержимому через доступные функции чтения и записи. Этот вид контроля доступа важен для гарантирования независимости от внутренностей класса и тем самым делает класс многократно используемым. Вы можете использовать классы для создания новых типов данных, таких, как наш простой register. Использование классов для создания новых типов данных является важной частью ООП. Вы можете также использовать их для инкапсуляции математических вычислений или создания динамических структур данных, таких как стеки, списки, очереди, и так далее. Инкапсуляция организации структур данных или сведений о вычислениях в классе делает структуру данных или вычислений многократно используемыми. В качестве более полного примера, давайте посмотрим на полезный тип данных, стек. Стек - это LIFO ("последним пришёл - первым обслужен") структура. Элементы помещаются в стек командой push(), а извлекаются из стека pop(). Pop () возвращает последний элемент, помещенный в стек, и удаляет его из структуры данных. Внутренние элемент stkptr следит за вершиной стека. Элемент, на который он указывает, это вершина стека, а все, что ниже его (то есть, с меньшим индексом) ниже в стеке. Ниже приведена базовая реализация стека в SystemVerilog


Файл:30.png

Файл:31.png

Файл:32.png

Файл:33.png

Класс stack инкапсулирует все, что нужно знать о стеке данных структуры. Он содержит интерфейс и реализацию интерфейса. Интерфейс – это набор методов, которые используются для взаимодействия с классом. Реализация - это код, который заставляет класс работать. Интерфейс нашего стека содержит следующие методы:

Файл:34.png

Не существует другого способа взаимодействия со стеком, как с помощью этих методов. Есть также два элемента данных класса, stk и stkptr, которые представляют фактическую структуру стека. Тем не менее, эти два элемента локальные, что означает, что компилятор запретит любые попытки доступа к ним за пределами класса. Для предотвращения доступа к внутренней структуре данных извне, мы можем сделать некоторые гарантии о состоянии данных. Например, Push () и Pop () могут рассчитывать на то, что stkptr корректно и указывает на вершину стека. Если бы можно было изменить значение stkptr иными средствами, чем с помощью интерфейса функций, тогда Push () и Pop () пришлось бы прибегнуть к дополнительным затратам и возможностям для ненадежной проверки истинности stkptr. Реализация интерфейса встроенная. Объявление класса содержит не только определение интерфейса, а также реализацию каждой функции интерфейса. Оба C + + и SystemVerilog позволяют отделить реализацию от интерфейса. Разделение интерфейса и реализации является важной концепцией. Программисты, пишущие на C + +, могут использовать файлы заголовков для ввода интерфейс и. куб (или. срр или любой другой компилятор использует) для реализации.

Есть некоторые важные явления доступа через класс интерфейсов. Одним из них является возможность многократного использования. Мы можем удобно использовать классы, интерфейсы которых четко определены и хорошо объяснены, чем те, чьи интерфейсы нечеткие. Другим важным явлением обеспечения доступа через класс интерфейсов является надежность. Авторы класса может гарантировать определенную инвариантность (например, stkptr меньше, чем размер имеющегося массива STK) когда они знают, что пользователи не будут изменять данные иначе, чем предоставляемыми средствами. Кроме того, пользователи могут ожидать, что состояние объекта будет предсказуемым, когда они придерживаются интерфейса. Ясность другое явление. Интерфейс может описать всю семантику класса. Объект не будет делать ничего другого, чем выполнять операции, доступные через интерфейс. Это делает проще понять для тех, кто использует класс, что именно он будет делать.


Отношения между объектами

Истинная сила ООП становится очевидной, когда объекты взаимосвязаны различными отношениями. Есть много видов отношений, которые возможны. Мы рассмотрим два из наиболее фундаментальных отношений HAS-A и IS-A.

HAS-A

HAS-A относится к концепции, когда один объект содержится или принадлежит другим. В нашем классе стека, например, HAS-A указатель стека (stkptr) и массив стека. Это примитивные типы данных, а не классы, но применяется тоже понятие HAS-A. В SystemVerilog вы можете создать HAS-отношения между классами с ссылками или указателями. На рисунке ниже показаны основные памяти модель для HAS-А отношений. Объект А содержит ссылку или указатель на Объект B.


35.png


Unified Modeling Language (UML) является графическим языком для представления систем, в частности, отношения между объектами в этих системах. UML для HAS-связи выражается линией между объектами и закрашенным ромбиком(??????), как показано на рисунке ниже.


36.png


Объект А принадлежит экземпляру объекта B. кодирование HAS-А отношений в SystemVerilog заключается в включении одного класса в другой или других способов предоставления доступа одного класса к одному классу, который хранится внутри.

Файл:37.png


Класс А содержит ссылку на класс B. конструктор для класса A, метод new(), вызывает new() в классе B, чтобы создать его экземпляр. Член b содержит ссылку на вновь созданный экземпляр B.


IS-A

IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики. Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс является подклассом или более специализированная версия родительского объекта. Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.


38.png

Животные, которые являются членами китообразных, хищных, приматов - млекопитающие. Эти очень разные виды существ имеют общие черты млекопитающих. Тем не менее, у китообразных (киты, дельфины), плотоядных (собак, медведей, енотов), и приматов (обезьян, людей), у каждого есть свои четкие и безошибочные характеристики. Для использования ОО терминологии, медведь - хищник и плотоядный IS- А млекопитающего. Иными словами, медведь состоит из атрибутов и плотоядные млекопитающие и плюс, дополнительные атрибуты, отличающие его от других плотоядных животных.

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


39.png


При представлении двух объектов в компьютерной программе с использованием наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана базовая модель памяти для IS-А композиции. В этом примере класс B является производным от А.


40.png


SystemVerilog использует ключевое слово extends на определения наследования между классами:

Файл:41.png


Класс B получена из A, поэтому он содержит все атрибуты A. Любой экземпляр B не только содержит строку S, но и объект с плавающей запятой F и целое i.

Виртуальные функции и полиморфизм

Одной из причин для создания объектов, используя наследование, является определение различного поведения для той же операции. Другими словами, поведение, определенное в производном классе переопределяет поведение, определенное в базовом классе. Это можно сделать, используя виртуальные функции. Виртуальная функция – функция, которую можно переопределить в производном классе. Рассмотрим следующий универсальный пакет класса.


Файл:42.png

Файл:43.png


Она имеет три виртуальных функций, чтобы установить содержимое пакета. Различные виды пакетов, требуют различного содержания. Мы используем generic_packet как базовый класс и получают различные виды пакетов из него.

Файл:44.png

Оба packet_A и packet_B могут иметь различные заголовки и трейлеры и различные форматы полезной нагрузки. Информация о том, как части пакета отформатированы, хранится локально внутри производных классов пакета. Виртуальные функции set_header (), set_trailer (), и set_body () реализованы по-разному в каждом подклассе основываясь на типе пакета. Базовый класс generic_packet устанавливает организацию классов и типы операций, которые возможны, и производные классы могут изменить поведение этих операций.

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

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


45.png


figure является базовым классом; polygon является производным от figure; square происходит от polygon. Каждый класс имеет две функции, draw(), которая является виртуальной, и compute_area (), которая не является виртуальной. Следующий пример показывает, SystemVerilog код:

Файл:46.png

Файл:47.png

Каждая функция выводит его полное имя в виде class_name :: имя_функции. Мы можем написать простую программу, которая показывает, как виртуальные функции связаны.

Файл:48.png

Ниже показано, что происходит, когда мы запускаем эту программу:

49.png

Сначала мы создаем s, квадрат, а затем назначаем его на f и р. базовым классом для square является polygon и базовым классом polygon - figure. Из напечатанного вывода, мы можем заключить, что функции связаны в соответствии со следующей таблице:

50.png


Во всех случаях, compute_area () была связана с конкретным compute_area () функцией с типом ссылки, которая ее определяет -р ссылки на polygon, таким образом связаны polygon :: compute_area (). Так как compute_area () не является виртуальной функцией. Компилятор может легко определить, какие версии функций вызывать, основываясь на типе объекта.

Так как draw() является виртуальной, не всегда возможно для компилятора  определить, какую функцию вызывать. Решение принимается на этапе выполнения с помощью 

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

Обратите внимание, что хотя р представляет собой polygon, вызов p.draw () приводит к square::draw() вызывается не polygon::draw(), как вы могли бы ожидать. То же самое происходит и с F-f.draw () связан с кsquare::draw(). объект, который мы первоначально создан представляет собой square, он поддерживает различные типы операций но, то, что это square не забывается. Это работает только потому, что square является производным от polygon, который в свою очередь является производным figure, и потому draw() объявлен как виртуальный. Ошибка компиляции О несовместимости типа возникает при попытке присвоить ей к s

и p   так как с не получено от р.