Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие С++- не книжкой_новое.doc
Скачиваний:
4
Добавлен:
04.11.2018
Размер:
765.44 Кб
Скачать

Глава 3. Основы объектно-ориентированного программирования

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

Описать класс возможно следующим образом:

class имя_класса {список_элементов};

Элементы касса (компоненты класса member) делятся на поля (данные-члены, элементы данных), которые представляют собой данные и методы (компонентные функции, функции-члены), которые представляют собой функции для работы с данными.

Синтаксис описания полей класса в целом соответствует синтаксису описания переменных. Однако имеются некоторые ограничения. Поля класса могут иметь любой тип, кроме типа того же класса (но могут быть указателями на этот класс). Инициализация полей при описании не допускается. Поля могут быть описаны со спецификатором const, в этом случае они будут инициализироваться один раз (с помощью специального метода - конструктора) и не могут изменяться в дальнейшем. Кроме того, у поля либо не указывается класс памяти, либо может указываться только static.

Синтаксис описания методов класса в целом соответствует синтаксису описания функций. Метод можно объявить как константный (метод, который не может менять значения полей класса). В этом случае указывается спецификатор const после списка параметров. Рекомендуется описывать как константные методы, которые предназначены для получения значений полей.

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

  • private (элементы видимые только внутри класса – скрытые элементы),

  • public (элементы видимые как внутри так и вне класса – открытые элементы – интерфейс класса),

  • protected (элементы, которые видимы только внутри класса и наследникам класса – защищенные элементы).

По умолчанию вид доступа – private. Действие любого спецификатора распространяется до следующего спецификатора и можно задавать несколько секций спецификаторов, причем порядок их следования не имеет значения.

Например,

class имя_класса

{

private:

описание скрытых элементов

public:

описание доступных элементов

};

Приведем пример описания класса «Строка».

class CStr

{

char * s; // поле для хранения строки

int len; // поле для хранения длины строки

public:

CStr () {len=0; s=new char; *s=’\0’;} // метод создания пустой строки

CStr (char *); // метод создания строки, равной заданной

char * get_str() const {return s;} //метод получения строки

int get_len() const {return len;}// метод получения длины строки

}

В данном классе два скрытых поля и четыре доступных метода. Причем тело одного из методов -CStr (char*) - не определено внутри класса.

Если тело метода определяется внутри класса, то он называется встроенными (inline). Обычно встроенными делают только короткие методы. Если тело метода описывается вне класса, то используется операция изменения видимости (::). Например,

CStr::Cstr(char * st)

{len=strlen(st); s=new (char[len+1]); strcpy(s,st); s[len]=’\0’;}

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

имя_класса имя_объекта [(список параметров)]; // список не может быть пустым

имя_класса (список параметров); // создается объект без имени, список может быть пустым

имя_класса имя_объекта= выражение; // создается объект без имени и копируется

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

СStr s1; // создание объекта класса СStr – пустых строк

CStr s2(“aaa”); //создание объекта класса СStr – строки «aaa» с длиной 3

CStr *s3=&s2;//указатель на объект s2

CStr s4=СStr(“bbb”); //создается безымянный объект со значением строки строки «bbb» и длиной 3 и копируется в создаваемый объект s4;

Можно также создать константный объект, значения полей которого изменять запрещается. К нему должны применяться только константные методы, например.

const CStr er(“Error”);

Конструктор – это специальный метод класса, имя которого совпадает с именем класса. Именно конструктор вызывается автоматически при создании объекта класса. В каждом классе есть хотя бы один конструктор. Если он не описан программистом, то создается автоматически. Конструктор не возвращает значения, даже типа void и не наследуется. Конструкторы нельзя описывать со спецификаторами const, virtual, static. Класс может содержать несколько конструкторов с разными типами параметров. Конструктор без параметров или конструктор, все параметры которого имеют значение по умолчанию, называют конструктором по умолчанию. Параметры конструктора могут иметь любой тип кроме типа этого же класса. Один из конструкторов может иметь значения параметров по умолчанию. При задании нескольких конструкторов следует соблюдать те же правила что и при описании перегруженных функций – у компилятора должна быть возможность распознать нужный конструктор по типу параметров.

Для инициализации в конструкторе полей-констант, полей ссылок и полей – объектов используют следующий способ, который можно применять и ко всем прочим полям. После списка параметров и до тела конструктора ставят двоеточие и проводят инициализацию полей через запятую. Например, конструктор CStr () можно переопределить следующим образом:

CStr (): len (0) {s=new char; *s=’\0’;}

Специальным видом конструктора является конструктор копирования. Его единственным параметром является указатель на объект этого же класса:

имя класса (const имя класса&){тело}

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

Пример конструктора копирования для класса CStr

CStr::CStr (const CSrt &А)

{

len= strlen(A.s);

s=new char[strlen(A.s)+1];

strcpy(s, A.s);

}

В каждом классе есть метод особого вида, называемый деструктором, который применяется для освобождения памяти, выделенной под объект. Имя деструктора начинается с тильды ~ за которой следует имя класса. Деструктор не имеет аргументов и не возвращает значения, не наследуется. Если деструктор явным образом не определен, то автоматически создается компилятором. Деструктор автоматически вызывается, когда объект выходит из области действия. Описывать деструктор в классе явным образом требуется только в том случае, когда объект содержит указатели на память, выделяемую динамически.

Пример деструтора для класса CStr

СStr::~Csrt(){delete [] s};

Доступ к элементам класса осуществляется обычно с помощью операции уточненного имени

имя объекта. имя элемента,

Например,

cout<<s3.get_str()<<s3.get_len();

Если определен указатель на объект, то можно использовать операцию ->, например

cout<<s4->get_str()<<s4->get_len();

Внутри каждого метода неявным образом используется указатель this - это константный указатель на объект, вызвавший метод. Он передается в метод как скрытый параметр. В явном виде указатель this применяется в основном для возращения из метода указателя (return this) или ссылки (return *this) на вызвавший метод объект. Например, рассмотрим метод, сравнивающий длину двух строк и возвращающий строку, имеющую максимальную длину.

CStr & long (CStr & A)

{

if (len>A.get_len()) return *this;

return A;

}

Пример вызова метода:

CStr a(“aaaa”), b(“bbb”);

CStr max=a.long(b);

Иногда желательно иметь непосредственный доступ к полям извне к скрытым полям класса, то есть расширить интерфейс класса. Для этого используются дружественные функции. Они объявляются внутри класса со спецификатором friend и должны иметь в качестве параметра объект или ссылку на объект. Дружественная функция может быть обычной функцией или методом другого класса, определенного ранее. На нее не распространяется действие спецификаторов доступа. Одна функция может быть дружественной сразу нескольким классам.

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

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

Синтаксис описания функции-операции

тип operator операция (список параметров) {тело}

Например, опишем операцию удаления из строки последнего символа

class CStr

{ … CStr & operator --() {s[len-1]=’\0’; --len; return *this;} };

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

class CStr

{ bool operator = =(const CStr & st)

{if (strcmp (s, st.get_str())==0) return true; return false; }

}

Приведем пример функции -операции, являющейся дружественной двум классам.

friend ostream& operаtor << (ostream& out, CStr& st )

{return out<<st.s;}

Таким образом, нами описан следущий класс CStr

class CStr

{

protected:

char* s; int len;

public:

CStr(); CStr(char*);

CStr(char ); CStr(const CStr&);

CStr& operator=(const CStr&);

bool operator ==(CStr &);