ТОП 10:

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



 

Основными свойствами ООП являются инкапсуляция, наследование и полиморфизм. Инкапсуляцией называется объединение данных с функциями их обработки в сочетании со скрытием не нужной для использования этих данных информации. Инкапсуляция позволяет использовать класс в другом окружении и создавать библиотеки классов для применения во многих программах. Наследование – это возможность создания иерархии классов, когда потомки (производные классы) наследуют все свойства своих предков (базовых классов), могут их изменять и добавлять новые. Свойства при наследовании повторно не описываются, что сокращает объём программы. Третьим китом, на котором стоит ООП, является полиморфизм – возможность использовать в различных классах иерархии одно имя для обозначения сходных по смыслу действий и гибко выбирать требуемое действие во время выполнения программы.

Для работы с собственными типами данных требуются специальные функции. Естественно сгруппировать их с описанием этих типов данных в одном месте, а также отделить от её остальных частей.

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

class Имя {

private:

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

public:

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

};

Спецификаторы доступа private и public управляют видимостью элементов класса. Элементы, описанные после служебного слова private, видимы только внутри класса. Интерфейс класса (методы) описывается после спецификатора public и доступен за пределами класса. Действие любого спецификатора распространяется до следующего спецификатора или до конца класса. Здесь раскрывается ключевая особенность ООП – возможность сокрытия данных, т.е. данные могут быть заключены внутри класса и защищены от несанкционированного доступа функций, расположенных вне класса.

Поля класса:

– могут иметь любой тип, кроме типа этого же класса (но могут быть указателями или ссылками на этот класс);

– могут быть описаны с модификатором const, при этом они инициализируются только один раз (с помощью конструктора) и не могут изменяться.

Инициализация полей при описании не допускается! Классы могут быть глобальными (объявленными вне любого блока) и локальными (объявленными внутри блока, например, функции или другого класса).

Приведём простой пример, содержащий класс и два объекта этого класса:

class SmallObj { // îïðåäåëåíèå êëàññà

private:

int data; // ïîëå êëàññà

public:

void setdata(int d) { // ìåòîä êëàññà, èçìåíÿþùèé çíà÷åíèå ïîëÿ

data = d;

}

void showdata() { // ìåòîä êëàññà, îòîáðàæàþùèé íà ýêðàí çíà÷åíèå ïîëÿ

cout << " Znachenie polya ravno " << data << endl;

}

};

//---------------------------------------------------------------------------

int main() {

SmallObj s1, s2; // îïðåäåëåíèå äâóõ îáúåêòîâ êëàññà SmallObj

s1.setdata(2001); // âûçîâ ìåòîäà setdata (инициализация)

s2.setdata(2002);

s1.showdata(); s2.showdata(); // âûçîâ ìåòîäà showdata (вывод на экран)

getch(); return 0;

}

Класс SmallObj, определённый в этой программе, содержит одно поле данных и два метода. Методы обеспечивают доступ к полю данных класса. Нельзя обратиться к полю класса из главной функции, поскольку доступ к полям возможен только из методов класса. Объединение данных и функций является стержневой идеей ООП (см. рис.).

Класс    

Объект находится в таком же отношении к своему классу, в каком переменная находится по отношению к своему типу. Как правило, скрывая данные класса, его методы оставляют доступными. Это объясняется тем, что данные скрывают с целью избежать нежелательного внешнего воздействия на них, а функции, работающие с этими данными, должны обеспечивать взаимодействие между данными и внешней по отношению к классу частью программы. Тем не менее, не существует чёткого правила, которое бы определяло, какие данные следует определять как private, а какие функции – как public.

Вы можете столкнуться с ситуациями, когда будет необходимо скрывать функции и обеспечивать свободный доступ к данным класса. Например, если поле класса сделать открытым public: int data; , то в главной функции можно получать доступ к этому полю по аналогии со структурами s2.data = 25; .

Из приведённого выше примера видно, что для получения доступа к методу класса необходимо использовать операцию точки (.), связывающую метод с именем объекта. Синтаксически это напоминает доступ к полям структуры, но в данном случае мы совершаем вызов функции, а не используем значение переменной. Оператор s1.setdata(2001); вызывает метод setdata() объекта s1 . Метод присваивает полю somedata объекта s1 значение, равное 2001. Аналогично, два вызова функции showdata() отобразят на экране значения полей соответствующих объектов.

Рассмотрим пример создания класса, основой для которого послужит структура Tovar, рассматривавшаяся нами ранее и содержащая информацию о товарах на складе.

const int N=30;

class Tovar {

private:

char name[N];

int number;

float cena;

public:

void setdata(char* tname, int tnum, float tcen) {

strcpy(name, tname);

number = tnum;

cena = tcen;

}

void showdata() {

cout << "\nNaimenovanie tovara: " << name;

cout << "\nNomer tovara: " << number;

cout << "\nStoimost tovara: " << cena;

}

};

int main() {

Tovar t1, t2;

t1.setdata("Sanki der.", 1100, 231.45); //инициализация значений

t1.showdata();

t2.setdata("Konki beg.", 2211, 134.90);

t2.showdata();

getch(); return 0;

}

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

class Tovar {

private:

public:

void setdata(char* tname, int tnum, float tcen) {

}

void getdata() {

cout << "\n Vvedite naimenovanie tovara: "; cin.getline(name, N);

cout << " Vvedite nomer tovara: "; cin >> number;

cout << " Vvedite stoimost tovara: "; cin >> cena;

}

void showdata() {

}

};

int main() {

Tovar t1, t2, t3;

t3.getdata();//позволяет вводить данные пользователем

t3.showdata();

getch(); return 0;

}

Конструкторы

Как правило, удобнее инициализировать поля объекта автоматически в момент его создания, а не явно вызывать в программе соответствующий метод. Такой способ инициализации реализуется с помощью особого метода класса, называемого конструктором. Конструктор – это метод класса, предназначенный для инициализации объекта и выполняющийся автоматически в момент создания объекта. У конструкторов есть несколько особенностей. Во-первых, имя конструктора в точности совпадает с именем класса, что позволяет компилятору отличить конструктор от других методов класса. Во-вторых, у конструкторов не существует возвращаемого значения (даже типа void). Это объясняется тем, что конструктор автоматически вызывается системой, и, следовательно, не существует вызывающей программы или функции, которой конструктор мог бы возвратить значение.

Приведём пример, в котором вместо метода setdata() используется два конструктора (без аргументов и с аргументами).

class Tovar {

private:

enum { N = 30 }; // static const int N = 30;

char name[N];

int number;

float cena;

public:

Tovar() {

//name[0]='\0'; или strcpy(name,"\0"); number = 0; cena=0.;

cout << "\n Konstruktor 1" << endl;

}

Tovar(char* tname, int tnum, float tcen) {

strcpy(name, tname);

number = tnum;

cena = tcen;

cout << "\n Konstruktor 2" << endl;

}

void getdata() {

}

void showdata() {

}

};

int main() {

Tovar t1("Sanki der.", 1100, 231.45), t2;

t2.getdata();

//t2=t1; //такая операция допускается

t1.showdata(); t2.showdata();

getch(); return 0;

}

В данном примере, прослеживается такое явление, как перегрука конструкторов. Перегрузка уже встречалась нам при изучении функций. Компилятор определяет требуемый для запуска конструктор по числу и характеру (типу) аргументов. Для объекта t1 запускается конструктор 2, а для t2 – конструктор 1.

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

class Distance {

private:

int feet;

float inch;

public:

Distance(): feet(0), inch(0.0) // êîíñòðóêòîð áåç àðãóìåíòîâ

{ }

Distance(int ft, float in): feet(ft), inch(in) // êîíñòðóêòîð ñ äâóìÿ àðãóìåíòàìè

{ }

void getdist() { // ââîä äëèíû ïîëüçîâàòåëåì

cout << "\n Vvedite chislo futov: "; cin >> feet;

cout << " Vvedite chislo duymov: "; cin >> inch;

}

void showdist() { // âûâîä äëèíû íà ýêðàí

cout << feet << "'-" << inch << "''";

}

void add_dist(Distance a1, Distance a2); // ïðîòîòèï

};

//-----Îïðåäåëåíèå ìåòîäîâ êëàññà âíå êëàññà (ñëîæåíèå äëèí)

void Distance::add_dist(Distance a1, Distance a2) {

inch = a1.inch + a2.inch; // Ñëîæåíèå äþéìîâ

feet = 0; // ñ âîçìîæíûì çà¸ìîì

if(inch >= 12.0) { // Åñëè ÷èñëî äþéìîâ áîëüøå 12.0, òî

inch = inch - 12.0; // óìåíüøàåì ÷èñëî äþéìîâ íà 12.0 è

feet++; // óâåëè÷èâàåì ÷èñëî ôóòîâ íà 1.

}

feet += a1.feet + a2.feet; // Ñëîæåíèå ôóòîâ

}

//---------------------------------------------------------------------------

int main() {

Distance d1, d2(11, 6.25), d3; // îïðåäåëåíèå è èíèöèàëèçàöèÿ d2

d1.getdist(); // ââîä çíà÷åíèé äëÿ d1

d3.add_dist(d1, d2); // d3=d1+d2

cout << "\n d1 = "; d1.showdist();

cout << "\n d2 = "; d2.showdist();

cout << "\n d3 = "; d3.showdist();

getch(); return 0;

}

В этом примере приведён альтернативный синтаксис конструкторов, который аналогичен уже рассматривавшемуся ранее и более эффективному:

Distance() {

feet = 0; inch = 0;

}

Distance(int ft, float in) {

feet = ft;

inch = in;

}

Основной блок этой программы начинается с присвоения начальных значений полям объекта d2 класса Distance, после чего производится его сложение с экземпляром d1, инициализируемым пользователем. На экран выводится следующее:

Vvedite chislo futov: 11

Vvedite chislo duymov: 6.25

d1 = 11'-6.25"

d2 = 11'-6.25"

d3 = 23'-0.5"

Перегруженные конструкторы

Из программы видно, что инициализация переменной d2 класса Distance производится в момент её создания, путём вызова конструктора с аргументами:

Distance(int ft, float in): feet(ft), inches(in) // конструктор с двумя аргументами

Здесь мы инициализируем поля feet, inches теми значениями, которые передаются конструктору в качестве аргументов.

Если же для нас важно, какими значениями будут инициализироваться поля объектов класса (d1 и d3) при объявлении, то следует явно определить конструктор, что и сделано в рассматриваемой программе:

Distance(нет аргументов): feet(0), inches(0.0) { } // конструктор без аргументов

Здесь члены класса инициализируются константными значениями (0), что позволяет быть уверенным в не случайности значений.

Итак, в программе имеется два явно определённых конструктора с одним и тем же именем Distance(). В этом случае говорят, что конструктор является перегруженным. Какой из этих двух конструкторов исполняется во время создания нового объекта, зависит от того, сколько аргументов используется при вызове. Спроси у студентов, какие конструкторы вызываются:

Distance dlina; // вызывает 1-ый конструктор

Distance shirina(11, 6.2); // вызывает 2-ой конструктор

 







Последнее изменение этой страницы: 2016-04-08; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.233.221.149 (0.024 с.)