Логическое программирование
Основная идея - базирование на логике первого порядка с четко определенной семантикой и синтаксисом. Программист должен записать аксиомы, описывающие задачу. На сегодняшний день нет языка, который полностью бы поддерживал этот тип программирования, из-за ограничения логики первого порядка. Язык реализации Пролог.
Объектно-ориентированное программирование (ООП)
В 1980 году было официально объявлено о создании языка Smalltalk-80, разработанного в научно- исследовательском центре фирмы Xerox. Этот язык поддерживал абсолютно новую по тому времени концепцию объектно-ориентированного программирования. Он стал родоначальником существующих языков программирования этого класса.
Основная идея - реальные объекты обладают тремя базовыми характеристиками: имеют набор свойств, способны разными методами изменять эти свойства и реагировать на события, возникающие как внутри, так и вне объекта.
Объекты, имеющие идентичную структуру и отличающиеся значениями свойств, объединяются в классы. Каждый конкретный объект является экземпляром класса.
Положения ООП (Алан Кей).
- Все является объектом.
- Вычисления осуществляются путем взаимодействия (обмена данными) между объектами, при котором один объект требует, чтобы другой объект выполнил некое действие. Объекты взаимодействуют, посылая и получая сообщение. Сообщение - это запрос на выполнение действия, дополненный набором аргументов, которые могут понадобиться при выполнении действия.
- Каждый объект имеет независимую память, которая состоит из других объектов.
- Каждый объект является представителем класса, который выражает общие свойства объектов.
- В классе задается поведение (функциональность) объекта. Тем самым все объекты, которые являются экземплярами одного класса, могут выполнять одни и те же действия.
- Классы организованы в единую древовидную структуру с общим корнем, называемую иерархией наследования. Память и поведение, связанное с экземплярами определенного класса, автоматически доступны любому классу, расположенному ниже в иерархическом дереве.
Благодаря тому, что программа представляется в терминах поведения объектов, при программировании используются понятия, более близкие к предметной области, а следовательно программа легче читается и понимается.
Тема 3. 3. Основы объектно-ориентированного программирования на С++
Основные свойства ООП
Основными свойствами ООП являются инкапсуляция, наследование и полиморфизм.
Объединение данных с функциями их обработки в сочетании со срытием ненужной для использования этих данных информации называется инкапсуляцией (encapsulation). Инкапсуляция повышает степень абстракции программ. Первыми шагами на пути у инкапсуляции были механизмы, повышающие степень абстракции, такие как использование функций, типов данных, определяемых пользователем и модулей. Принцип инкапсуляции в ООП реализуются с помощью механизма классов. Класс является абстрактным типом данных, определяемым пользователем, и содержит описание данных и функций для работы с этими данным.
Описать класс возможно следующим образом:
class имя_класса {список_элементов};
Элементы касса (компоненты класса member) делятся на поля (данные-члены, элементы данных), которые представляют собой данные и методы (компонентные функции, функции-члены), которые представляют собой функций для работы с данными.
Поля класса могут иметь любой тип, кроме типа того же класса. Инициализация полей при описании не допускается.
В классе можно использовать одни из спецификаторов, управляющих видимостью элементов класса.
private – видимы только внутри класса
public – видимы вне класса.
protected (только для построения иерархии классов).
По умолчанию вид доступа – private. Действие любого спецификатора распространяется до следующего спецификатора и можно задавать несколько секций спецификаторов.
class CStr
{
char * s;
int len;
public:
CStr () {len=0; s=new char; *s=’\0’;}
CStr (char *);
char * get_str() {return s;}
int get_len(){return len;}
}
Если тело метода определяется внутри класса, то он называется встроенными (inline). Обычно встроенными делают только короткие методы. Если тело метода описывается вне класса, то используется операция изменения видимости.
CStr::Cstr(char * st)
{len=strlen(st); s=new (char[len+1]); strcpy(s,st); s[len]=’\0’;}
Переменные, имеющие тип описанного класса принято называть объектами. Для описания объектов класса в общем случае используется конструкция
имя_класса имя_объекта [(список параметров)];
В каждом классе есть хотя бы один метод, имя которого совпадет с именем класса. (Если он не описан программистом, то создается автоматически). Он называется конструктором и именно он вызывается автоматически при создании объекта класса. Конструктор не возращает значения, дажет типа void. Класс может содержать несколько конструкторов с разными типами параметров. При создании объекта выделятся память, необходимая для хранения всех его полей и конструктор обычно выполняет их инициализацию. Методы класса не тиражируются.
СStr s1,s2;
CStr s3(“aaa”);
При выходя из области действия объект уничтожается, при этом вызывается деструктор.
Для доступа к элементам класса осуществляется обычно с помощью операции уточненного имени
имя объекта. имя элемента
cout<<s1.get_str();
Если определен указатель на объект, то можно использовать операцию ->.
Благодаря тому, что программа представляется в терминах поведения объектов, при программировании используются понятия, более близкие к предметной области, а следовательно программ легче читается и понимается.
В каждом классе есть метод особого вида, называемый деструктором, который применяется для освобождения памяти, выделенной под объект. Имя деструктора начинается с тильды ~ за которой следует имя класса. Деструктор не имеет аргументов и не возвращает значения, не наследуется. Если деструктор явным образом не определен, то автоматически создается компилятором. Деструктор автоматически вызывается, когда объект выходит из области действия. Описывать деструктор в классе явным образом требуется только в том случае, когда объект содержит указатели на память, выделяемую динамически.
СStr::~Csrt(){delete [] s};
В С++ можно переопределить большинство операций так, чтобы при использовании с объектами конкретного типа они выполняли заданные функции. Это дает использовать собственные типы данных точно также как стандартные. Перегрузка операций осуществляется с помощью методов специального вида (функций-операторов) и подчинятся следующим правилам:
- при перезагрузке операций сохраняется количество аргументов, приоритеты операций и правила ассоциации;
- для стандартных типов данных переопределять операции нельзя;
- функции операции наследуются (кроме =), не могут иметь аргументы по умолчанию, не могут определяться как static;
- функция – операция может быть либо методом класса, либо дружественной функцией, либо обычно функцией, но в последних двух случая она должна принимать хотя бы одни аргумент, имеющий тип класса, указателя или ссылки на класс.
bool operator ==(CStr & s1, CStr& s2)
{
if (s1.get_len()==s2.get_len())
{ int i=0;
while ( i<s1.get_len() && s1.get_str()[i]==s2.get_str()[i]) i++;
if (i==s1.get_len()) return true;
}
return false;
}
Иногда желательно иметь непосредственный доступ к полям извне к скрытым полям класса, то есть расширить интерфейс класса. Для этого используются дружественные функции. Они объявляются внутри класса со спецификатором friend и должны иметь в качестве параметра объект или ссылку на объект. Дружественная функция может быть обычной функцией или методом другого класса. На нее не распространяется действие спецификаторов доступа. Одна функция может быть дружественной сразу нескольким классам.
friend ostream& operаtor << (ostream& out, CStr& st )
{return out<<st.s;}
Отметим, что использование дружественных функций нарушает принцип инкапсуляции и таким образом затрудняет отладку программы.
Наследование – это возможность создания иерархии классов, в которой потомки (производные классы, наследники) получают элементов своих предков (родителей, базовых классов), могут их изменять и добавлять новые. Различают простое и множественное наследование (каждый производный класс может иметь только одного родителя или нескольких родителей соответственно). В С++ каждый класс может иметь сколько угодно потомков и предков.
Синтаксис описания класса-наследника
сlass имя : [private| protected | public] имя базового класса
По умолчанию private (делает все элементы private). Public - не изменяет доступа. Protected - делает все поля protected, кроме private.
class CBStr: public CStr
{
public:
CBStr(char* a);
CBStr(char a);
};
Если поля родителя имеют тип private, то работы с ними в классе – наследнику необходимо использовать методы базового класса или объявить их явным образом в наследнике в секции public. Следующим образом,
class CBStr: public CStr
{
public:
СStr::s;
….
};
Кроме того, если функция производного класса требуется работать с полями базового, то в базовом классе такие поля можно описать как protected.
Конструкторы и деструкторы и операция присваивания не наследуются. Если в конструкторе производного класса явный вызов конструктора базового класса отсутствует, то автоматически вызывает конструктор базового класса без параметров. Можно явно вызывать конструктор базового класса следующим образом.
CBStr::CBStr(char* a):CStr (a)
{
if (!binarnost(a)) empty();
}
где binarnost() – функция проверки строки на бинарность, empty () – метод базового класса, делающий строку пустой.
void CStr::empty()
{ if (*s!= '\0') { len = 1; delete []ch; ch = new char; ch[0] = '\0';} }
Если в базовом классе поля описаны как protected, то к ним можно ображаться в производном классе.
Если не описан деструктор в производном классе, что он формируется автоматически и вызывает деструкторы всех базовых классов.
Полиморфизм - это возможность использовать в различных классах иерархии одно имя для обозначения различных по смыслу действий. Например, сложение строк – это конкатенация, а сложение бинарных строк – это бинарное сложение.
Объектно-ориентированный подход в программировании целесообразно применять, если требуется построить именно иерархию классов, иначе можно ограничиться модульной технологий программирования.