40гг первые цифровые компьютеры программирование путем коммутации проводов
Вид материала | Документы |
- Рабочая программа учебной дисциплины «Системы коммутации» Направление подготовки, 204.68kb.
- Неоднородный полупроводниковый носитель информации в переменном магнитном поле, 107.68kb.
- Темы Лекции Практика, 13.65kb.
- Общие принципы построения вычислительных сетей, 1480.56kb.
- Курс лекций "интернет технологии", 1261.62kb.
- А) Представление информации в цифровых автоматах (ЦА), 34.28kb.
- Информатика. Лекции. Краткая история компьютерной техники Первые компьютеры: Z3, Colossus,, 3630.67kb.
- Курс лекций для студентов очного и заочного отделений по специальности 210406 «Сети, 3045.9kb.
- Радумльская средняя школа, 28.4kb.
- Лекция №7. Обобщенная задача коммутации Важной задачей построения сетей ЭВМ является, 67.2kb.
int month, day, year;
public:
void set(int, int, int);
void get(int*, int*, int*);
void next();
void print();
};
date.cpp
#include “date.h”
void date::print()
{
printf(“%i.%i.%i”,day,
month,year);
}
…
Конструкторы
- Использование для обеспечения инициализации объекта класса функций вроде set_date() (установить дату) неэлегантно и чревато ошибками.
- Поскольку нигде не утверждается, что объект должен быть инициализирован, то программист может забыть это сделать, или (что приводит, как правило, к столь же разрушительным последствиям) сделать это дважды.
- Есть более хороший подход: дать возможность программисту описать функцию, явно предназначенную для инициализации объектов.
- Поскольку такая функция конструирует значения данного типа, она называется конструктором.
- Конструктор распознается по тому, что имеет то же имя, что и сам класс.
- У конструктора отсутствует возвращаемое значение.
Создание и использование конструктора
class date{
private:
int month, day, year;
public:
date(int day, int month, int year);
date(char *str); // дата в строковом представлении
date(int day); // день, месяц и год сегодняшние
…
};
date d1; // ошибка
date d2(5,5,2005);
date *d3 = new date(6,6,2006);
Деструктор
- Определяемый пользователем тип чаще имеет, чем не имеет, конструктор, который обеспечивает надлежащую инициализацию.
- Для многих типов также требуется обратное действие, деструктор, чтобы обеспечить соответствующую очистку объектов этого типа.
- Имя деструктора для класса X есть ~X() ("дополнение конструктора").
- В частности, многие типы используют некоторый объем памяти из динамической памяти, который выделяется конструктором и освобождается деструктором.
- Деструктор вызывается когда переменная выходит из области видимости (для класса памяти auto) или при использовании оператора delete.
Пример деструктора
class char_stack{
private:
int size;
char* top;
char* s;
public:
char_stack(int sz)
{ top=s=newchar[size=sz]; }
~char_stack(){ delete[] s; } // деструктор void push(char c) { *top++ = c; }
char pop() { return *--top;}
};
Inline
- При программировании с использованием классов очень часто используется много маленьких функций.
- Это может страшно понизить эффективность, потому что стоимость вызова функции (хотя и вовсе не высокая по сравнению с другими языками) все равно намного выше, чем пара ссылок по памяти, необходимая для тела функции.
- Чтобы справиться с этой проблемой, был разработан аппарат inline- функций. Метод, определенный (а не просто описанная) в описании класса, считается inline.
- Это значит, что при компиляции программы вызов функции будет заменен на подстановку ее тела в место вызова.
Ссылки на себя
- В методе на атрибуты, для которого он был вызван, можно ссылаться непосредственно.
- Указатель на объект, для которого вызван метод, является скрытым параметром метода. На этот неявный параметр можно ссылаться явно как на this.
- При ссылке на атрибуты использование this излишне.
- Главным образом this используется при написании методов, которые манипулируют непосредственно указателями.
Перегрузка операторов и функций
Перегрузка функций
- Бывает удобно, чтобы функции, реализующие один и тот же алгоритм для различных типов данных, имели одно и то же имя.
- Если это имя мнемонично, то есть несет нужную информацию, это делает программу более понятной, поскольку для каждого действия требуется помнить только одно имя.
- Использование нескольких функций с одним и тем же именем, но с различными типами параметров, называется перегрузкой функций.
- Компилятор определяет, какую именно функцию требуется вызвать, по типу передаваемых параметров.
- Этот процесс называется разрешением перегрузки (перевод английского слова resolution в смысле «уточнение»). Тип возвращаемого функцией значения в разрешении не участвует.
- Механизм разрешения основан на достаточно сложном наборе правил, смысл которых сводится к тому, чтобы использовать функцию с наиболее подходящими аргументами и выдать сообщение, если такой не найдется.
Пример перегрузки функций
Имеется четыре варианта функции, определяющей наибольшее значение:
// Возвращает наибольшее из двух целых:
int max(int, int);
// Возвращает подстроку наибольшей длины:
char* max(char*. char*);
// Возвращает наибольшее из первого параметра и длины второго:
int max (int, char*);
// Возвращает наибольшее из второго параметра и длины первого:
int max (char*, int);
Вызов различных функций
- При вызове функции max компилятор выбирает соответствующий типу фактических параметров вариант функции (в приведенном примере будут последовательно вызваны все четыре варианта функции).
void f(int a, int b, char* C; char* d){
cout « max (a, b) « max(c, d) « max(a. c) « max(c, b);
}
Неоднозначности при вызове перегруженных функций
- Неоднозначность может появиться при:
- преобразовании типа;
- использовании параметров-ссылок;
- использовании аргументов по умолчанию.
Пример неоднозначности при преобразовании типа
#include
float f(float i){
cout « "function float f(float i)" « endl;
return i;
}
double f(double i){
cout « "function double f(double i)" « endl;
return i*2;
}
int main(){
float x = 10.09;
double у = 10.09;'
cout « f(x) « endl; // Вызывается f(float)
cout « f'(y) « endl; // Вызывается f(double)
/* cout « f(10) « endl; Неоднозначность - как преобразовать 10: во float или double? */
return 0;
}
Пример неоднозначности при использовании параметров-ссылок
- Функции
int f(int a, int b);
int f(int a, int &b);
- Вызов
int a = 5, b=10;
f(a,b);
- Компилятор не сможет узнать, какая из этих функций вызывается, так как нет синтаксических различий между вызовом функции, которая получает параметр по значению, и вызовом функции, которая получает параметр по ссылке.
Пример неоднозначности при использовании аргументов по умолчанию
#include
int f(int a){return a;}
int f(int a, int b=1){return a * b;}
int main(){
cout « f(10, 2); // Вызывается f(int. int)
/* cout « f(10); Неоднозначность - что вызывается: f(int, int) или f(int) */
return 0;
}
Правила описания перегруженных функций
- Перегруженные функции должны находиться в одной области видимости, иначе произойдет сокрытие аналогично одинаковым именам переменных во вложенных блоках.
- Перегруженные функции могут иметь параметры по умолчанию, при этом значения одного и того же параметра в разных функциях должны совпадать.
- В различных вариантах перегруженных функций может быть различное количество параметров по умолчанию.
- Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или использованием ссылки (например, int и const int или int и int&).
Перегрузка операторов
- Третье поколение ЯП – языки, предоставляющие программисту средства определения абстрактных типов данных
- С++, C#, Java
- Типы понимаются как множества с операциями.
- Хотелось бы, что бы одинаковые по смыслу операции обозначались бы одинаково не зависимо от того используется встроенный тип данных или тип данных определенный пользователем.
- В этом случае операции, обозначаемые одним и тем же символом будут обозначать приводить к выполнению совершенно различных действий в зависимости от контекста.
Перегруженный оператор “+”
int a,b;
double c,d;
char *e,*f;
a = a + b;
с = с + d;
e = f + a;
string s1= “abc”, s2 = “def”, s3;
s3 = s1 + s2; // s3 = “abcdef”;
Правила перегрузки операций
- Перегрузка операций осуществляется с помощью функций специального вида (функций-операций)
- Функция-операция содержит ключевое слово operator, за которым следует знак переопределяемой операции:
- тип operator операция ( список параметров) { тело функции }
- при перегрузке операций сохраняются количество аргументов, приоритеты операций и правила ассоциации (справа налево или слева направо), используемые в стандартных тинах данных;
- для стандартных типов данных переопределять операции нельзя;
- функции-операции не могут иметь аргументов по умолчанию;
- функции-операции наследуются (за исключением =);
- функции-операции не могут определяться как static.
- Функцию-операцию можно определить тремя способами:
- как метод класса,
- дружественная функция класса,
- либо обычной функцией.
Переопределение операции с помощью метода класса
class Square{
private:
int x,y;
int size;
public:
Square(){x = 0; y = 0; size = 10;}
print(){printf(“ x=%i, y= %i, size=%i\n”,x,y,size);}
Square &operator++() {size++; return *this;}
…
};
…
Square s;
(++s).print();
…
Переопределение операции с помощью обычной функции
class Square{
private:
int x,y;
public:
int size;
Square(){x = 0; y = 0; size = 10;}
print(){printf(“ x=%i, y= %i, size=%i\n”,x,y,size);}
…
};
Square &operator++(Square &s) {s.size++; return s;}
…
Square s;
(++s).print();
…
Переопределение операции с помощью функции - друга
class Square{
private:
int x,y;
int size;
public:
Square(){x = 0; y = 0; size = 10;}
print(){printf(“ x=%i, y= %i, size=%i\n”,x,y,size);}
friend Square &operator++(Square &s);
…
};
Square &operator++(Square &s) {s.size++; return s;}
Square s;
(++s).print();
…
Унарная постфиксная операция
- Операции постфиксного инкремента и декремента должны иметь первый параметр типа int.
- Он используется только для того, чтобы отличить их от префиксной формы:
class Square{
private:
int x,y;
int size;
public:
Square(){x = 0; y = 0; size = 10;}
print(){printf(“ x=%i, y= %i, size=%i\n”,x,y,size);}
Square &operator++(int) {size++; return *this;}
…
};
…
Square s;
s++;
s.print();
…
Перегрузка бинарных операций
- Бинарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода с параметрами, при этом вызвавший ее объект считается первым операндом.
- Если функция определяется вне класса, она должна иметь два параметра типа класса.
class Square{
private:
int x,y;
int size;
public:
Square(){x = 0; y = 0; size = 10;}
print(){printf(“ x=%i, y= %i, size=%i\n”,x,y,size);}
bool operator>(const Square &s)
{
if(size >s.size)
return true;
else
return false;
}
…
};
…
Square s1,s2;
if(s1 > s2) printf(“s1 больше!”);
…
class Square{
private:
int x,y;
int size;
public:
Square(){x = 0; y = 0; size = 10;}
print(){printf(“ x=%i, y= %i, size=%i\n”,x,y,size);}
friend bool operator>(const Square &s1, const Square &s2);
…
};
bool operator>(const Square &s1, const Square &s2)
{
if(s1.size >s2.size)
return true;
else
return false;
}
…
Square s1,s2;
if(s1 > s2) printf(“s1 больше!”);
…
Перегрузка операции присваивания
- Операция присваивания определена в любом классе по умолчанию как поэлементное копирование.
- Она вызывается каждый раз, когда одному существующему объекту присваивается значение другого.
- Если класс содержит поля, память под которые выделяется динамически, необходимо определить собственную операцию присваивания.
- Чтобы сохранить семантику присваивания, операция-функция должна возвращать ссылку на объект, для которого она вызвана, и принимать в качестве параметра единственный аргумент — ссылку на присваиваемый объект.
Семантика средств описания действий
Разработка программ
- Основная задача, стоящая перед каждым программистом, — получение правильной программы.
- Гарантировать ее правильность может только тщательное пошаговое уточнение (разработка сверху вниз) и одновременное доказательство того, что все вычисления в программе дают корректные результаты за конечное время.
- Этот способ реализуется в два этапа:
- проведение декомпозиции общей задачи на точно определенные подзадачи и доказательство того, что если каждая подзадача решена корректно и полученные решения связаны друг с другом определенным образом, то задача будет решена корректно;
- повторение декомпозиции и доказательство ее корректности до достижения подзадач настолько простых, что их решение выражается в нескольких строках языка программирования.
- Чтобы доказать корректность программы, необходимо формально описать семантику операторов языка.
Основные понятия
- Статическая структура программы представляется ее текстом на языке программирования, описывающем действия, которые должны быть выполнены при решении данной задачи.
- Динамическая структура отражает процесс вычислений и состоит из последовательности состояний вычислений.
- Состояние вычислений в любой момент времени включает значения всех переменных в этот момент и, таким образом, зависит от начальных значений переменных и предшествующих этапов вычислений программы.
- Если статическая структура не зависит от исходных данных, то динамическая структура программы в значительной степени определяется выбором исходных данных, поскольку при выполнении программы будут сделаны различные переходы в зависимости от значений входных переменных.
- Текущий оператор
- изменяет состояние вычислений (значения некоторых переменных) и передает управление следующему оператору,
- или производит проверку состояния вычислений (сравнивает значения определенных переменных) и на основе результата этой проверки передает управление другому оператору.
- Язык программирования предоставляет как простые операторы — операторы действия, так и средства композиции — операторы управления, которые позволяют формировать составные операторы из других простых или составных.
Правила вывода
- Для описания семантики языка необходимо иметь правила вывода, позволяющие определить
- эффект воздействия простого оператора на состояние вычислений
- свойства составного оператора, исходя из свойств его компонент.
- Фундаментальное свойство всех описываемых здесь способов композиции заключается в том, что они позволяют объединить в один составной оператор с одним входом и одним выходом один или более операторов также с одним входом и одним выходом и не позволяют строить операторы с несколькими входами или выходами.
- Именно это свойство дает возможность в достаточно простой форме описывать действия любого оператора или программы.
Спецификация программы
- Пусть S — какой-нибудь оператор или программа, Р и Q — предикаты, описывающие состояние вычислений. Тогда семантика S описывается следующим образом:
{P} S {Q}
- Это — спецификация программы, означающая: если предикат Р истинен перед выполнением S, то предикат Q будет истинен после завершения выполнения S.
- Предикат Р называется предусловием S, а предикат Q — постусловием S.
Свойства спецификаций программ
- Для любых S и Р всегда найдется некоторый предикат Q (например, true), который будет постусловием S, если Р — предусловие S
- Для любого S и любого Q всегда найдется некоторый предикат Р (например, false), который будет предусловием S, если Q — постусловие S.
- Для любых Р и Q всегда найдется программа S (например, while true do; end do;), для которой Р будет предусловием, a Q — постусловием.
- Задача программиста — найти такую программу S, которая завершается и для которой Р является предусловием, a Q — постусловием.
Частичная и полная корректность программы
- Если S — программа, корректность которой мы хотим установить, то соотношение {Р} S {Q} — это то, что мы должны доказать, где Р — условие, которому должны удовлетворять начальные значения переменных, a Q — условие, которому должны удовлетворять конечные значения переменных.
- Соотношение {Р} S {Q} определяет только частичную корректность S.
- Программа, в которой гарантируется получение требуемого результата при условии ее завершения, частично корректна.
- Если дополнительно можно показать, что программа завершается при всех исходных данных, удовлетворяющих Р, то программа полностью корректна.
- Чтобы доказать завершимость программы, необходим анализ циклов.
Процесс разработки программы
- Нисходящий процесс разработки программ можно сформулировать следующим образом:
- построение спецификации программы {Р} S {Q}, т.е. ясное и недвусмысленное определение того, как программа должна использоваться (предусловие Р) и какие результаты (постусловие Q) должны быть получены;
- определение на каждом этапе декомпозиции спецификаций {Pj} Sj {Qj} для компонент Sj программы;
- разработка программы и ее компонент одновременно с доказательством корректности указанных спецификаций.
Запись предикатов
- При записи предикатов Р и Q могут быть использованы следующие средства:
- Имена переменных, арифметические операции, операции сравнения.
- Математические символы и функции.
- Логические операции: конъюнкция,
дизъюнкция, отрицание и т.д.
- Кванторы всеобщности (), существования () и количества (N). Каждое вхождение переменной X в логическое выражение, которое не связано квантором, называется свободным, а X — свободной переменной.
Подстановки
- Пусть Р — логическое выражение. Обозначение (1) используется для выражения, которое получается в результате подстановки выражения у вместо всех свободных вхождений переменной х в Р.
- Аналогично (2) обозначает одновременную подстановку вместо всех свободных вхождений любой переменной хi в Р cоответствующего выражения уi Вхождения xi в уj не замещаются. Переменные х1,х2,… должны быть различными.
Правила вывода
- Правила вывода, задающие семантику языка программирования, в общем случае это схемы рассуждений, позволяющие доказывать свойства программы, т.е. из некоторых предпосылок, входных условий получать утверждение о спецификации программы.
- Если Н1,Н2,…,Нn — истинные утверждения, то Н — также истинное утверждение. Если входных условий нет (т.е. n=0), то правило вывода будем записывать просто в виде H
Простейшие правила вывода
- Первое утверждает, что если выполнение программы S обеспечивает истинность утверждения R, то оно также обеспечивает истинность каждого утверждения, которое следует из R.
- Второе правило утверждает, что если R — известное предусловие программы S, приводящей к результату Q после завершения своего выполнения, то это же относится к любому другому утверждению, из которого следует R.
- Эти два правила называются правилами консеквенции.
Правила тавтологии
- Правила тавтологии выводятся непосредственно из определения спецификации программы
- Для любых P,Q, S