Динамическое распределение памяти.
Ответы по проге
Понятие о дружественности.
При помощи специального спецификатора доступа можно указать, имеют ли функции, не являющиеся элементами данного класса, доступ к его элементам. Может возникнуть ситуация, когда нужно обеспечить определенной функцие или классу доступ к элементам данного класса, объявленным при помощи privateили protected. Это делается при помощи ключевого слова friend:
friendclassA
friend void B(float c)
Правила:
1)Если А объявил Б другом, это не значит, что Б объявил другом А.
2)Если А объявил Б другом, то наследники Б не получают автоматически доступ к А.
3)Если А объявил Б другом, то наследники А не будут автоматически признавать другом Б.
Динамическое распределение памяти.
Классы памяти:
-автоматические
-регистровые
-статические
-внешние
Распределения:
-статическое
-динамическое
В C++ существуют специальные операции для управления динамическим распределением памяти: newи delete – для простых переменных и new[] и delete[] – для массивов. Также сохранены стандартные функции C – malloc(), calloc(), realloc(), free().
int *A;
A=new int;
*A=125;
cout<<*A;
delete A;
A=new int[100];
delete [] A;
А также для простых переменных: int *A=newint(456);
Классы могут определять свои собственные операции newи delete(new[] и delete[]). Синтаксис их перегрузки не отличаетсяот обычной перегрузки операций:
void *A:: operatornew(…)
{…};
Если newили deleteвызывается для представителя класса, то одновременно с ними вызывается конструктор или деструктор, соответственно. Для переменных, память под которые выделена таким образом, конструктор не выделяет, а деструктор не освобождает память, так как это делают newи delete.
Инкапсуляция и полиморфизм.
Под инкапсуляцией понимают наличие механизма, позволяющего объединять некоторым скрытым от пользователя образом данные и функции для обработки этих данных. (Как пример: предположим, что мы написали игру и нам совсем не хочется, чтобы какой-то левый ум покопался в нашем коде, что-то там искал или пытался понять как мы это сделали. Поэтому используем специальные механизмы, не позволяющие пользователю лезть в наш код, то есть – если хочешь, пожалуйста, пользуйся, играй в нашу игру, но тебе для этого не надо знать, как мы ее делали. Как раз тот факт, что существуют такие механизмы и позволяет нам говорить о понятии инкапсуляции.)
Под полиморфизмом понимается способность некоторых механизмов (например, функций) по-разному реагировать на внешне одинаковый запрос. (Пример: виртуальные функции, перегрузка функций.)
Наследование классов.
C++ позволяет классу наследовать элементы данных и элементы-функции одного или нескольких других классов, новый класс называют производным (сын), класс, элементы которого наследуются, называется базовым (отец). Производный класс, в свою очередь, может быть базовым для другого класса.
Наследование относится к базовым принципам объектно-ориентированного программирования (инкапсуляция, полиморфизм, наследование).
Производный класс может переопределить некоторые функции-элементы базового, наследуя, тем не менее, основной объем свойств базового класса.
class A{…}; class B: ключA {…};
Ключ, находящийся в заголовке класса насоедника может быть private, publicили protected. Если его нет, то по умолчанию используется privateдля классов и publicдля структур. Этот ключ оказывает влияние только на доступ к элементам отца через представитель сына, при этом он не оказывает влияния на доступ к элементам отца из тела функции класса сына. Для определения можно или нельзя через представитель сына обратиться к унаследованным элементам отца используется более жёсткий спецификатор из ключа, стоящего в теле отца, и ключа, стоящего в заголовке сына. (То есть: при public, стоящем в заголовке сына мы можем из, например, функции main, используя представитель класса сына обратиться к publicэлементам отца, также мы можем обращаться к public и protectedэлементам отца из тела сына; при privateмы не можем обращаться к элементам отца ниоткуда; и наконец, при protected, мы можем обратиться к public и protectedэлементам отца из тела класса сына, а также при таком наследовании все publicэлементы отца наследуются сыном со сменой спецификатора на protected, то есть будующие наследники от уже существующего, обращаясь к этим элементам, будут обращаться к ним как к protected, а не как к public). (И да, это полная ***а L).
При наследовании не происходит наследования конструкторов и деструкторов отца, таким образом, чтобы использовать конструктор отца, его необходимо вызвать при создании конструктора сына, причем он будет вызываться в списке инициализации конструктора сына:
classA {…A(параметры);…}; classB: ключA {... B(параметры): A(параметры);…}
С++допускает сложное наследование – одновременное наследование от нескольких классов:
class C: ключA, ключB {…};
При этом может оказаться так, что класс унаследует несколько элементов одного и того же класса (То есть: например, классB– наследник класса A, а класс C – наследник классов A и B. Следовательно класс Cунаследует элементы класса А, элементы класса В и снова элементы класса А, потому что их наследовал класс В).
Виртуальные функции.
Механизм, использование которого демонстрирует полиморфизм. Пример: представим, что у нас есть класс и его наследник. При этом и в наследнике и в базовом классе существует функция Plus(параметры) с одинаковыми параметрами. Также в отце есть функция, которая во время своей работы вызывает функцию Plus(). И если мы во время работы вызовем вторую функцию отца, та в свою очередь станет вызывать функцию Plus() отца, так как у нее нет доступа к функциям сына. Но, благодаря виртуальным функциям мы можем сделать так, чтобы она вызвала функцию сына, а не отца. Для этого нужно добавить ключевое слово virtualперед именем функции Plus() как при описании ее в отце, так и при описании ее в сыне. Тогда будет вызываться функция сына, а не отца. Получается, что мы заместили функцию отца функцией сына.
Переопределить-заместить виртуальные функции можно только в наследниках класса, в котором прописаны эти виртуальные функции. Замещающие функции должны иметь тот же список параметров, что и замещаемая функция, при этом виртуальные функции могут замещать невиртуальные функциибазового класса. Очень важно, что при объявлении в некотором классе функции виртуальной, в классах-потомках, переопределяющих эту функцию, она также будет виртуальной, но только если она имеет тот же список параметров. Если переопределяющая функция в классе потомке имеет другой список параметров, то ее версия из базового класса будет не доступна классу потомку и всем его потомкам.
Виртуальные функции могут быть объявлены чистыми:
Virtualтип имя (параметры) = 0;
Такие функции создаются в базовом классе затем, чтобы потом переопределяться в классах, производных от него.
Класс, объявляющий только чисто виртуальные функции (одну и более), называется абстрактным базовым классом. У такого класса нельзя создать представитель, а служит он только для дальнейшего порождения классов, которые будут реализовывать его чисто виртуальные функции.
Существует возможность объявлять ссылки на абстрактные классы. Введение в С++ абстрактных классов позволяет строить иерархию классов более чётко.
Прочность модулей.
Прочность модуля – это мера его внутренних связей.
1)Модуль прочный по совпадению – это модуль, между элементами которого нет осмысленных связей. Такие модули возникают при разбиении на модули уже после написания программы.
2)Модуль прочный по логике – при каждом вызове выполняет выбранную функцию из набора связанных с ним.
3)Модуль прочный по классу – последовательно выполняет набор связанных с ним функций.
4)Процедурно-прочный модуль – последовательно выполняет набор тех связанных с ним функций, которые непосредственно относятся к процедуре решения задачи.
5)Коммуникационно-прочный модуль – процедурно-прочный модуль, с одним дополнительным ограничением. Все его функции связаны по данным.
6)Информационно-прочный модуль – выполняет несколько функций, причем все они работают с одной и той же структурой данных и каждая представляется собственным кодом.
7)Функционально-прочный модуль – выполняет одну определенную функцию, которая характеризуется как способом передачи, так и свойствами самих этих данных.
Сцепление модулей.
Сцепление модулей – мера взаимодействия модулей по данным, которая характеризуется как способом передачи данных, так и свойствами самих этих данных.
1)Два модуля сцеплены по содержимому, если один прямо ссылается на содержимое другого.
2)Группа модулей сцеплена по общей области, если они ссылаются на одну и ту же глобальную структуру данных.
3)Группа модулей сцеплена по внешним данным, если они ссылаются на один и тот же глобальный элемент данных.
4)Два модуля сцеплены по управлению, если один явно управляет функционированием другого.
5)Группа модулей сцеплена по формату, если они ссылаются на одну ту же не глобальную структуру данных.
6)Двамодуля сцеплены по данным если один вызывает другой и все входные и выходные параметры вызываемого модуля – простые элементы данных.
Дополнение в 9-10 вопросам.
В дополнение к прочности и сцеплению есть и другие характеристики оказывающие воздействие на независимость модулей. Это:
•Размеры модуля (~ от 10 до 100 операторов языка программирования).
•Предсказуемость модуля (модуль не должен хранить следы своих состояний при последующих вызовах).
•Минимизация доступа к данным.
•Отсутствие внутренних процедур.
При проектировании структуры программы имеется несколько стратегий разбиения на модули. Причем эти стратегии применяются последовательно. Разбиение «исток – преобразование – сток» предполагает деление задачи на функции, занимающиеся получением данных, изменением их формы, и затем доставкой их в некоторую точку вне задачи.
Функциональное разбиение – это деление задачи на функции, выполняющие конкретные преобразования данных.
Стрессовое тестирование.
Под стрессовым тестированием понимается проверка программ большими объемами данных, которые зачастую удобнее сгенерировать компьютером. Большие объемы сами по себе могут стать причинами сбоев, вызывая переполнение буферов ввода, массивов, счетчиков. Большие объемы данных также очень полезны при поиске неоправданных ограничений в размерах структур данных.
Если при тестировании вводятся только осмысленные реалистичные данные, то большинство ошибок ввода никогда не случится и, следовательно, обрабатывающий их код не будет исполнен, а он может быть ошибочным.
Но важно не доводить дело до абсурда и не тратить время на исправление ошибок в ситуациях, когда их возникновение крайне маловероятно.
Некоторые виды тестов основаны на введении преднамеренно некорректных данных, так как любой не контролируемый ввод является потенциальной лазейкой для взлома системы.
Еще одним фактором, на который нужно обратить внимание при тестировании переполнений является преобразование типов.
Хорошие тесты и тестовые случаи часто могут быть использованы для большого количества программ. Например, каждая программа, читающая файлы, должна быть проверена вводом пустого файла. Каждая программа, читающая текст, должна быть оттестирована двоичным вводом, каждая программа, читающая строки текстов, должна быть проверена вводом очень длинных строк, пустых строк и файлами без употребления символа перевода строки вообще.
Автоматизация тестирования.
При большом количестве тестов, которые необходимо выполнить, полезно написать программу, которая последовательно запускает эти тесты.
Одной из основных форм автоматизации является возвратное тестирование, при котором выполняется последовательность тестов, сравнивающих очередную новую версию программы с предыдущей. Основное назначение возвратного тестирования – убедиться в том, что поведение программы изменилось только в предусмотренных рамках. В дополнение к возвратным тестам полезно использовать и замкнутые тесты, которые содержат в себе и вводимые данные, и ожидаемые результаты.
Существуют также инструменты, которые взаимодействуют с программой во время ее выполнения. Их основная цель – измерить качество тестирования, определить, в какой мере тесты проверяют алгоритм программы. Например, можно контролировать выполнение каждой подпрограммы, подсчитывая, сколько раз она вызывалась. Можно также учесть сколько раз выполняется каждый оператор исходной программы, или во всех ли направлениях выполняется каждый условный переход.
Часто такая статистика накапливается в отдельном файле или базе данных, что полезно при комплексном тестировании системы. Для автоматизации тестов разрабатываются также специальные языки.
Ответы по проге
Понятие о дружественности.
При помощи специального спецификатора доступа можно указать, имеют ли функции, не являющиеся элементами данного класса, доступ к его элементам. Может возникнуть ситуация, когда нужно обеспечить определенной функцие или классу доступ к элементам данного класса, объявленным при помощи privateили protected. Это делается при помощи ключевого слова friend:
friendclassA
friend void B(float c)
Правила:
1)Если А объявил Б другом, это не значит, что Б объявил другом А.
2)Если А объявил Б другом, то наследники Б не получают автоматически доступ к А.
3)Если А объявил Б другом, то наследники А не будут автоматически признавать другом Б.
Динамическое распределение памяти.
Классы памяти:
-автоматические
-регистровые
-статические
-внешние
Распределения:
-статическое
-динамическое
В C++ существуют специальные операции для управления динамическим распределением памяти: newи delete – для простых переменных и new[] и delete[] – для массивов. Также сохранены стандартные функции C – malloc(), calloc(), realloc(), free().
int *A;
A=new int;
*A=125;
cout<<*A;
delete A;
A=new int[100];
delete [] A;
А также для простых переменных: int *A=newint(456);
Классы могут определять свои собственные операции newи delete(new[] и delete[]). Синтаксис их перегрузки не отличаетсяот обычной перегрузки операций:
void *A:: operatornew(…)
{…};
Если newили deleteвызывается для представителя класса, то одновременно с ними вызывается конструктор или деструктор, соответственно. Для переменных, память под которые выделена таким образом, конструктор не выделяет, а деструктор не освобождает память, так как это делают newи delete.
Инкапсуляция и полиморфизм.
Под инкапсуляцией понимают наличие механизма, позволяющего объединять некоторым скрытым от пользователя образом данные и функции для обработки этих данных. (Как пример: предположим, что мы написали игру и нам совсем не хочется, чтобы какой-то левый ум покопался в нашем коде, что-то там искал или пытался понять как мы это сделали. Поэтому используем специальные механизмы, не позволяющие пользователю лезть в наш код, то есть – если хочешь, пожалуйста, пользуйся, играй в нашу игру, но тебе для этого не надо знать, как мы ее делали. Как раз тот факт, что существуют такие механизмы и позволяет нам говорить о понятии инкапсуляции.)
Под полиморфизмом понимается способность некоторых механизмов (например, функций) по-разному реагировать на внешне одинаковый запрос. (Пример: виртуальные функции, перегрузка функций.)
Наследование классов.
C++ позволяет классу наследовать элементы данных и элементы-функции одного или нескольких других классов, новый класс называют производным (сын), класс, элементы которого наследуются, называется базовым (отец). Производный класс, в свою очередь, может быть базовым для другого класса.
Наследование относится к базовым принципам объектно-ориентированного программирования (инкапсуляция, полиморфизм, наследование).
Производный класс может переопределить некоторые функции-элементы базового, наследуя, тем не менее, основной объем свойств базового класса.
class A{…}; class B: ключA {…};
Ключ, находящийся в заголовке класса насоедника может быть private, publicили protected. Если его нет, то по умолчанию используется privateдля классов и publicдля структур. Этот ключ оказывает влияние только на доступ к элементам отца через представитель сына, при этом он не оказывает влияния на доступ к элементам отца из тела функции класса сына. Для определения можно или нельзя через представитель сына обратиться к унаследованным элементам отца используется более жёсткий спецификатор из ключа, стоящего в теле отца, и ключа, стоящего в заголовке сына. (То есть: при public, стоящем в заголовке сына мы можем из, например, функции main, используя представитель класса сына обратиться к publicэлементам отца, также мы можем обращаться к public и protectedэлементам отца из тела сына; при privateмы не можем обращаться к элементам отца ниоткуда; и наконец, при protected, мы можем обратиться к public и protectedэлементам отца из тела класса сына, а также при таком наследовании все publicэлементы отца наследуются сыном со сменой спецификатора на protected, то есть будующие наследники от уже существующего, обращаясь к этим элементам, будут обращаться к ним как к protected, а не как к public). (И да, это полная ***а L).
При наследовании не происходит наследования конструкторов и деструкторов отца, таким образом, чтобы использовать конструктор отца, его необходимо вызвать при создании конструктора сына, причем он будет вызываться в списке инициализации конструктора сына:
classA {…A(параметры);…}; classB: ключA {... B(параметры): A(параметры);…}
С++допускает сложное наследование – одновременное наследование от нескольких классов:
class C: ключA, ключB {…};
При этом может оказаться так, что класс унаследует несколько элементов одного и того же класса (То есть: например, классB– наследник класса A, а класс C – наследник классов A и B. Следовательно класс Cунаследует элементы класса А, элементы класса В и снова элементы класса А, потому что их наследовал класс В).
Виртуальные функции.
Механизм, использование которого демонстрирует полиморфизм. Пример: представим, что у нас есть класс и его наследник. При этом и в наследнике и в базовом классе существует функция Plus(параметры) с одинаковыми параметрами. Также в отце есть функция, которая во время своей работы вызывает функцию Plus(). И если мы во время работы вызовем вторую функцию отца, та в свою очередь станет вызывать функцию Plus() отца, так как у нее нет доступа к функциям сына. Но, благодаря виртуальным функциям мы можем сделать так, чтобы она вызвала функцию сына, а не отца. Для этого нужно добавить ключевое слово virtualперед именем функции Plus() как при описании ее в отце, так и при описании ее в сыне. Тогда будет вызываться функция сына, а не отца. Получается, что мы заместили функцию отца функцией сына.
Переопределить-заместить виртуальные функции можно только в наследниках класса, в котором прописаны эти виртуальные функции. Замещающие функции должны иметь тот же список параметров, что и замещаемая функция, при этом виртуальные функции могут замещать невиртуальные функциибазового класса. Очень важно, что при объявлении в некотором классе функции виртуальной, в классах-потомках, переопределяющих эту функцию, она также будет виртуальной, но только если она имеет тот же список параметров. Если переопределяющая функция в классе потомке имеет другой список параметров, то ее версия из базового класса будет не доступна классу потомку и всем его потомкам.
Виртуальные функции могут быть объявлены чистыми:
Virtualтип имя (параметры) = 0;
Такие функции создаются в базовом классе затем, чтобы потом переопределяться в классах, производных от него.
Класс, объявляющий только чисто виртуальные функции (одну и более), называется абстрактным базовым классом. У такого класса нельзя создать представитель, а служит он только для дальнейшего порождения классов, которые будут реализовывать его чисто виртуальные функции.
Существует возможность объявлять ссылки на абстрактные классы. Введение в С++ абстрактных классов позволяет строить иерархию классов более чётко.