Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык программирования Сpp 25.09.11 (2).doc
Скачиваний:
16
Добавлен:
19.08.2019
Размер:
10.09 Mб
Скачать

7.1. Классы и объекты

Когда мы впервые знакомились с программами, то сказали, что программа на С++ начинается с функции main(), затем уточнили, что на самом деле программа начинается с директив препроцессора. Но и это не совсем верно. Есть программы, которые начинаются со слова class. Да и называются такие программы классами. Сразу возникает вопрос – зачем нужны какие то классы? Ответить на этот вопрос пока не так просто. Но все же кое-что сказать можно.

Редактируя документы в известной программе "MS Word" каждый раз, создавая чистое окно, вы заставляете компьютер создавать новую программу. Компьютер не обладает творческими способностями, и может создавать новые программы, используя определенные ранее прототипы или, лучше сказать, шаблоны. Вот они и есть классы. Кроме понятия класс нам потребуется связанное с ним понятие - объект класса, или просто объект.

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

Представим себе, что имеются чертежи, а лучше сказать техническая документация по производству автомобиля, пусть это будет ВАЗ2110. Здесь слово ВАЗ2110 на самом деле является именем марки. Вместо слов марка мы применим слово класс, т.е ВАЗ 2110 это имя класса. По чертежам на автозаводе изготавливаются автомашины. Рассмотрим идеальный случай, когда по одним и тем же чертежам изготавливаются совершенно одинаковые автомобили. Каждый автомобиль представляет собой самостоятельный объект. Эти объекты нужно как-то различать, хотя бы для того, чтобы вести учет выпускаемой продукции. С этой, а также с некоторыми другими целями, каждому объекту присваивается идентификационный номер или VIN (verification identification namber). На самом деле, на автомобиле есть еще несколько номеров, но сейчас мы о них забудем. Идентификационный номер, по существу, является именем автомобиля, именно он участвует во всех документах, сопровождающих автомобиль. Созданные совершенно одинаковые автомобили поступают в продажу, заправленные разным количеством бензина и проехав разное расстояние. Тогда для покупателя эти автомобили будут отличаться начальной заправкой, начальным пробегом или, говоря иначе, начальными условиями. Покупатель использует объект в соответствии со своими потребностями, не интересуясь чертежами автомобиля, марками сталей и пластмасс, техническими условиями и ГОСТами, поставщиками комплектующих изделий и т.п. Более того, покупатель часто даже не знает, как устроен автомобиль, но это не мешает использовать его по своему назначению.

Аналогичным образом дело обстоит в программировании. Мы можем написать программу, которая в дальнейшем будет пригодна для использования в наших задачах. Этой программе присвоим имя, и поместим её в память компьютера. В дальнейшем будем называть такую программу классом. Это наши чертежи. Если по образу этой программы мы создадим новую программу со своим именем (VIN-ом) и в ней дадим переменным конкретные значения (начальный пробег), то такую новую программу будем называть объектом, - это экземпляр автомобиля. В дальнейшем можно пользоваться этим объектом, не обращаясь к самому классу. Таких экземпляров может быть много, но они должны отличаться именем. Подчеркнем еще раз, что в нашем распоряжении должны быть, по крайней мере, две программы, одна из них класс, его создает программист, другая - объект создается компилятором на основании этого класса.

Формальное определение классов и объектов мы дадим позднее, а сейчас попробуем создать какой-нибудь класс и получить объекты. Делается это довольно просто, что видно из следующего примера.

Создадим программу которая определяет количество дней в году по номеру года (високосный год содержит 366 дней), и оформим её в виде класса.

#include <iostream> //директивы остаются без изменения

using namespace std;

class clYear //программа начинается со слова class

// за которым следует имя класса

{ //далее идет тело класса

public: //зарезервированное слово, которое означает

//доступность членов класса

int days(int); //прототип функции которая будет описана позднее

} // Внимание! класс заканчивается точкой с запятой

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

Теперь запишем самостоятельную программу. Как обычно, она начинается с функции main(), в которой совершим на первый взгляд совершенно необычное действие - объявим класс (программу) clYear как тип данных.

int main(){

clYear obYear; //Объявление объекта obYear в виде

//переменной типа clYear

int numberYear=2000; //переменная, которая означает номер года

while(numberYear>0){ //далее предлагается ввести номер года

cout<<"\n Input number year " ;

cin>> numberYear;

cout<<"The "<<numberYear<<" consist "

<<obYear.days(numberYear) // функция класса вызывается через

// имя объекта и имя функции

// разделенных точкой

<<" days";

}

}

Дадим, наконец, описание самой функции, которая определяет количество дней в году.

int clYear::days(int n){ /*при описании функции указывается

возвращаемый тип имя класса

к которому принадлежит функция

двойное двоеточие, имя функции */

if(n%4==0) return 366; //деление нацело определяет

//високосный год

else return 365;

}

Описание функции можно переместить, поставив его перед функцией main.

Теперь обсудим результаты.

  1. Легко заметить, что описание класса начинается с зарезервированного слова class, после которого идет имя класса, а затем в фигурных скобках тело класса. Описание класса заканчивается точкой с запятой. Последнее нужно всегда помнить, т.к. это обстоятельство является неисчислимым количеством ошибок. Иначе говоря, описание класса выглядит следующим образом

class имя

{

тело класса

};

  1. В классе содержатся только прототипы функций. Сами функции описываются вне класса. Инициализация в описании класса запрещена.

  2. В основной программе создается новая переменная, тип которой соответствует имени класса, подобно тому, как это делается для переменных базовых типов.

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

Рассматривая этот пример, мы немного слукавили. Лукавство состоит в том, что класс, его метод и основная часть программы при компиляции сохраняются в одном файле. То есть мы показали только то, что главная функция main() успешно работает с классом, если тот хранится в том же файле. Но на самом деле мы обещали более мощные возможности. Вспомним, мы говорили о том, что класс может храниться где-то в другом месте вне файла программы, а мы можем воспользоваться классом для создания объекта. Не спешите, у нас еще будет возможность поговорить об этом, когда мы будем рассказывать о, так называемых, модулях. А сейчас продолжим рассказ о классах и объектах.

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

Из приведенного примера непонятно зачем нужно было создавать класс, когда все гораздо проще можно было сделать без него. Но цель примера состояла в том, чтобы показать, как создать класс. Вот более сложный пример:

#include<iostream.h>

#include<string.h>

class Book{/*Обратите внимание: код класса представляет собой

схематическое опиание некоторого шаблона по которому

будут строиться объекты */

public:

//переменные класса

char title[256];

char author[64];

float price1;

int number;

//функции или методы класса

void show_book(); //вывод информации об авторе и названии

float get_price(); //расчет стоимости партии

};

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

void main(){

Book b1, b2; // создаются два объекта класса Book

//задаем параметры первого объекта

strcpy(b1.title, "War and pice");

strcpy(b1.author, "Tolstoy");

b1.price1=1000; // обращение к члену объекта через оператор точка

b1.number=10;

//задаем параметры второго объекта

strcpy(b2.title, "C++");

strcpy(b2.author, "Straustrup");

b2.price1=500;

b2.number=20;

//

b1.show_book();

b2.show_book();

cout<< "\nbalans="<<b1.get_price()+b2.get_price();

char z;

cin>>z;

}

//описание функций класса

void Book::show_book(){cout<<author<<": "<<title<<endl;}

float Book::get_price(){return price1*number;}

Вот результат:

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

Сейчас обратимся к непонятной пока метке public:. На самом деле это не метка, а, так называемый, спецификатор, иначе говоря, зарезервированное слово, которое означает, что все члены класса стоящие после него являются доступными (публичными), т.е. к ним можно обращаться из других классов, или из функции main. Под словом обращаться, здесь подразумевается следующее: если это переменная, то ее значение может быль прочитано и/или изменено, если это метод, то он может быть вызван.

Возникает вопрос: если есть открытые члены класса, то, вероятно, есть и закрытые, Действительно это так. Закрытые члены класса отделяются специификаатором «private:». Доступ к закрытым членам класса ограничен. В частности к ним могут обращаться только другие методы того же класса и класса наследника. Впрочем, об этом подробно мы расскажем позднее.

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

На первый взгляд непонятно зачем нужен механизм защиты членов класса. Ответ простой: для того, чтобы не повредить класс. В самом деле, представьте себе, что в классе используется константа PI=3.1416 и переменная pi, значение которой может изменяться. Программист, создавая программы может ререпутать идентификаторы и вместо того, чтобы задать значение переменной изменит константу. Программа будет работать, но результат будет неверным. Именно для этого используются слова const и private.

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

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

Что дает инкапсуляция? Вспомним, что создавая обычную программу без использования классов, программист обязан держать в голове алгоритм, помнить имена переменных, констант и функций. При использовании классов программист не должен знать, как устроен класс и механизм его работы. Достаточно знать, какими методами обладает класс, и какие данные нужно использовать, чтобы обратиться к этим методам. Ярким примером обладания свойством инкапсуляции может являться легковой автомобиль. Большинство людей пользуются его возможностями (методами) не зная того как он работает.

Кстати говоря, попробуем ответить на вопрос: так ли уж нужно раскрывать все методы и данные класса для тех программистов, которые его будут использовать в своих программах? Ведь в автомобиле многие устройства управления его системами скрыты или вообще недоступны для водителя. Ответ, вероятно, ясен. Классы должны иметь возможность некоторые члены класса делать недоступными. Вот сейчас и посмотрим как это делается в С++.

Вы, конечно, обратили внимание на новое слово public,которое встретилось при описании класса. Это так называемый, спецификатор доступа. Всего существует 4 вида такого рода спецификаторов. Они носят названия private, protected, friend и уже встретившийся нам public. Спецификаторы доступа нужны для того, чтобы указать какие члены класса доступны пользователю, а какие нет. Кстати, по умолчанию все члены класса являются закрытыми.

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

Никаких требований к количеству данных или функций в секциях класса не предъявляется.

Спецификатор protected (защищенный) означает, что пользователям этого класса разрешается доступ к членам класса только через новые, производные от созданного, класса. Эта секция, как правило, является второй.

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

Синтаксис имеет вид:

Спецификатор friend (друг) предназначен для того, чтобы указать, что данной функцией или классом могут пользоваться другие функции или классы, отмеченные в специальном списке. Приведем пример. Предположим, что мы имеем некоторый класс, в котором необходимо знать является ли некоторый год високосным или нет. Это может быть, например, класс, описывающий историю спортивного движения. Известно, что олимпийские игры проводятся именно в високосном году. Пусть этот класс имеет имя Olimpic. Тогда для того, чтобы узнать могли ли быть олимпийские игры в определенном году можно воспользоваться классом clYear,который мы недавно рассматривали, но при этом сделаем в нем небольшие изменения.

class clYear {

friend int numberYear();

friend class Olimpic; //Класс друг

private: int days(int);/*Секция закрыта для всех,

кроме класса Olimpic */

};

Вот пример посложнее

#include <iostream.h>

#include <windows.h>

//using namespace std;

class DaysYear{

public:

void input();

void output();

void set(int newMonth, int newDay);

//int newMonth, int newDay – определяют дату

int getMonth();//возвращает значение месяца (1-январь, 2-февраль..

int getDay();//возвращает день месяца

private:

int month;

int day;

};

//===========================================================

void DaysYear::input(){

cout<<"Введите месяц ";

cin>>month;

cout<<"Введите день месяца ";

cin>>day;

}

void DaysYear::output(){

cout<<" месяц "<<month<<" день "<<day<<endl;

}

void DaysYear::set(int newMonth,int newDay){

month=newMonth;

day=newDay;

}

int DaysYear::getMonth(){

return month;

}

int DaysYear::getDay(){

return day;

}

//=========================================================

int main(){

DaysYear today, birthday;

SetConsoleOutputCP(1251);

cout<<"Введите текущую дату"<<endl<<endl;

today.input();

cout<<"Сегодня ";

today.output();

birthday.set(4,01);

cout<<"День дураков";

birthday.output();

if(today.getMonth()==birthday.getMonth() && today.getDay()==birthday.getDay())

cout<<"Чудаки украшают мир, а дураки губят его!"<<endl;

else cout<<"Желаем удачного дня"<<endl;

return 0;

}