Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

Книги: [Классика] [Базы данных] [Internet/WWW] [Сети] [Программирование] [UNIX] [Windows] [Безопасность] [Графика] [Software Engineering] [ERP-системы] [Hardware]

     

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

Лафоре Р.

Издано: Издательский дом "Питер"
ISBN: 5-94723-302-9
Твердый переплет, 928 стр.

Начало
Cодержание
Отрывок
[Заказать книгу в магазине "Мистраль"]

Отрывок

Объекты и классы

Простой класс

Объекты C++ и физические объекты

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

В процессе чтения этой главы рекомендуется при необходимости возвращаться к материалу главы 1 "Общие сведения".

Простой класс

Наш первый пример содержит класс и два объекта этого класса. Несмотря на свою простоту, он демонстрирует синтаксис и основные черты классов C++. Листинг программы SMALLOBJ приведен ниже .

// smallobj.cpp
// демонстрирует простой небольшой объект
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////

class smallobj               // определение класса
{
  private:
    int somedata;            // поле класса
  public:
    void setdata(int d)      // метод класса, изменяющий значение поля
        { somedata = d; }
    void showdata()          // метод класса, отображающий значение поля

{ cout << "Значение поля равно " << somedata << endl; }
};
//////////////////////////////////////////////////////////
int main()
{

  smallobj s1, s2;           // определение двух объектов класса smallobj
  s1.setdata(1066);          // вызовы метода setdata()
  s2.setdata(1776);
  s1.showdata();             // вызовы метода showdata()
  s2.showdata();
  return 0;
}

Класс smallobj , определенный в этой программе, содержит одно поле данных и два метода. Методы обеспечивают доступ к полю данных класса. Первый из методов присваивает полю значение, а второй метод выводит это значение на экран (возможно, незнакомые термины привели вас в недоумение, но скоро мы раскроем их смысл).

Рис. 6.1. Класс содержит данные и функции

Объединение данных и функций является стержневой идеей объектно-ориентированного программирования. Это проиллюстрировано на рис. 6.1.

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

В главе 1 мы говорили, что объект находится в таком же отношении к своему классу, в каком переменная находится по отношению к своему типу. Объект является экземпляром класса, так же, как автомобиль является экземпляром колесного средства передвижения. Класс smallobj определяется в начале программы SMALLOBJ. Позже, в функции main(), мы определяем два объекта s1 и s2, являющихся экземплярами класса smallobj.

Каждый из двух объектов имеет свое значение и способен выводить на экран это значение. Результат работы программы выглядит следующим образом:

Значение поля равно 1076 - вывод объекта s1

Значение поля равно 1776 - вывод объекта s2

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

Определение класса

Определение класса smallobj в приведенной выше программе выглядит следующим образом:

class smallobj              // определение класса
{
  private:
    int somedata;            // поле класса
  public:
    void setdata(int d)      // метод класса, изменяющий значение поля
      { somedata = d; }
    void showdata()          // метод класса, отображающий значение поля

{ cout << "Значение поля равно " << somedata << endl; }
};

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

private и public

Тело класса содержит два не встречавшихся раньше ключевых слова: private и public. Сейчас мы раскроем их смысл.

Ключевой особенностью объектно-ориентированного программирования является возможность сокрытия данных. Этот термин понимается в том смысле, что данные заключены внутри класса и защищены от несанкционированного доступа функций, расположенных вне класса. Если данные необходимо защитить какие-либо данные, то их помещают внутрь класса c ключевым словом private. Такие данные доступны данные;общедоступные (public)только внутри класса. Данные, описанные с ключевым словом public, напротив, доступны за пределами класса. Вышесказанное проиллюстрировано на рис. 6.2.

Зачем скрывать данные?

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

Рис. 6.2. Скрытые и общедоступные классы

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

Данные класса

Класс smallobj содержит всего одно поле данных somedata, имеющее тип int. Данные, содержащиеся внутри класса, называют данными-членами или полями класса. Число полей класса, как и у структуры, теоретически может быть любым. Поскольку перед описанием поля somedata стоит ключевое слово private, это поле доступно только внутри класса.

Методы класса

Методы класса - это функции, входящие в состав классаданные;класса;методы класса. Класс smallobj содержит два метода: setdata() setdata(), методи showdata(), метод. Тела обоих методов состоят из одного оператора, который записан на одной строке с фигурными скобками, ограничивающими тело функции. Разумеется, можно использовать и более традиционный способ оформления функций:

void setdata(int d)
{
somedata = d;
}
и
void showdata()
{
cout << "\nЗначение поля равно " << somedata;
}

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

Поскольку методы setdata() и showdata() описаны с ключевым словом public, они доступны за пределами класса smallobj. Мы покажем, каким образом можно получить доступ к этим функциям, чуть позже. На рис. 6.3 показан синтаксис определения класса.

Рис. 6.3. Синтаксис определения класса

Сокрытие данных и доступность функций

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

Методы класса внутри определения класса

Методы класса smallobj выполняют действия, типичные для методов классов вообще: они считывают и присваивают значения полям класса. Метод setdata() принимает аргумент и присваивает полю somedata значение, равное значению аргумента. Метод showdata() отображает на экране значение поля somedata.

Обратите внимание на то, что функции setdata() и showdata() определены внутри класса, то есть код функции содержится непосредственно в определении класса (здесь определение функции не означает, что код функции помещается в память. Это происходит лишь при создании объекта класса). Методы класса, определенные подобным образом, по умолчанию являются встраиваемыми (встраиваемые функции обсуждались в главе 5 "Функции"). Позже мы увидим, что функции внутри класса можно не только определять, но и объявлять, а определение функции производить в другом месте. Функция, определенная вне класса, по умолчанию уже не является встраиваемой.

Использование класса

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

Определение объектов

Первым оператором функции main() является

smallobj s1, s2;

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

Вызов методов класса

Следующая пара операторов осуществляет вызов метода setdata():

s1.setdata(1066);
s1.setdata(1776);

Эти операторы выглядят не так, как обычный вызов функции. Почему имена объектов s1 и s2 связаны с именами функций операцией точки (.)? Такой странный синтаксис объясняется тем, что вызов применим к методу конкретного объекта методов класса. Поскольку setdata() является методом класса smallobj, его вызов должен быть связан с объектом этого класса. Например, оператор

setdata(1066);

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

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

Оператор

s1.setdata(1066);

вызывает метод setdata() объекта s1. Метод присваивает полю somedata объекта s1 значение, равное 1066. Вызов

s2.setdata(1776);

подобным же образом присваивает полю somedata объекта s2 значение, равное 1776. Теперь мы имеем два объекта с различными значениями поля somedata, как показано на рис. 6.4.

Аналогично, два вызова функции showdata() отобразят на экране значения полей соответствующих объектов:

s1.showdata();
s2.showdata();

Сообщения

В некоторых объектно-ориентированных языках программирования вызовы методов называют сообщениями. Так, например, вызов

s1.showdata();

можно рассматривать как посылку сообщения объекту s1 с указанием вывести на экран свои данные. Термин сообщение не входит в число формальных терминов языка C++, однако его полезно держать в голове в процессе дальнейшего обсуждения. Представление вызова методов в виде сообщений подчеркивает независимость объектов как самостоятельных единиц, взаимодействие с которыми осуществляется путем обращения к их методам. Если обратиться к аналогии со структурой компании, приведенной в главе 1, то вызов метода будет похож на письмо к секретарю отдела продаж с запросом статистики о рынке компании в каком-либо регионе.

Объекты программы и объекты реального мира

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

Детали изделия в качестве объектов

Класс smallobj из предыдущего примера содержал только одно поле данных. Теперь мы рассмотрим более интересный пример. Мы создадим класс, основой для которого послужит структура, описывающая комплектующие изделия и ранее использовавшаяся в программе PARTS главы 4 "Структуры". Рассмотрим следующий пример - OBJPART:

// objpart.cpp
// детали изделия в качестве объектов
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////

class part                   // определение класса
{
  private:
  int modelnumber;           // номер изделия
  int partnumber;            // номер детали
  float cost;                // стоимость детали
  public:
    // установка данных
    void setpart(int mn, int pn, float c)
   {
      modelnumber = mn;
      partnumber = pn;
      cost = c;
    }
    void showpart()          // вывод данных
    {

cout << "Модель " << modelnumber;
cout << ", деталь " << partnumber;
cout << ", стоимость $" << cost << endl;
}
};
//////////////////////////////////////////////////////////

int main()
{
  part part1;                // определение объекта
                             // класса part
  part1.setpart(6244, 373, 217.55F); // вызов метода
  part1.showpart();                  // вызов метода
  return 0;
} 

В этой программе используется класс part. В отличие от класса smallobj, класс part состоит из трех полей: modelnumber, partnumber и cost. Метод класса setpart() присваивает значения всем трем полям класса одновременно. Другой метод, showpart(), выводит на экран содержимое полей.

В примере создается единственный объект класса part с именем part1. Метод setpart() присваивает его полям значения соответственно 6244, 373 и 217.55. Затем метод showpart() выводит эти значения на экран. Результат работы программы выглядит следующим образом:

Модель 6244, деталь 373, цена $217.55

Этот пример уже ближе к реальной жизни, чем SMALLOBJ. Если бы вы разрабатывали инвентаризационную программу, то, вероятно, создали бы класс, аналогичный классу part. Мы привели пример объекта C++, моделирующего реально существующий объект - комплектующие изделия.

Круги в качестве объектов

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

Наша программа будет представлять собой объектно-ориентированную версию программы CIRCSTRC главы 5 (как и в программе CIRCSTRC , вам будет необходимо включить функции консольной графики в ваш проект. Эти файлы можно загрузить с сайта издательства, адрес которого указан во введении к данной книге. Описание файлов содержится в приложении Д "Упрощенный вариант консольной графики". Кроме того, полезную информацию можно также найти в приложениях к вашему компилятору). В программе будут созданы три круга с различными параметрами, а затем они появятся на экране. Ниже приведен листинг программы CIRCLES:

// circles.cpp
// круги в качестве объектов
#include "msoftcon.h"       // для функций консольной графики
//////////////////////////////////////////////////////////
class circle                //графический объект "круг"
{
  protected:
    int xCo,yCo;             // координаты центра
    int radius;
    color fillcolor;         // цвет
    fstyle fillstyle;        // стиль заполнения
  public:                    // установка атрибутов круга
    void set(int x, int y, int r, color fc, fstyle fs)
    {

      xCo = x;
      yCo = y;
      radius = r;
      fillcolor = fc;
      fillstyle = fs;
    }
    void draw()                    // рисование круга
    {
      set_color(fillcolor);        // установка цвета и
      set_fill_style(fillstyle);   // стиля заполнения
      draw_circle(xCo,yCo,radius); // рисование круга
    }
};
//////////////////////////////////////////////////////////
int main()
{
  init_graphics();           // инициализация графики
  circle c1;                 // создание кругов
  circle c2;
  circle c3;
  // установка атрибутов кругов
  c1.set(15, 7, 5, cBLUE, X_FILL);
  c2.set(41, 12, 7, cRED, O_FILL);
  c3.set(65, 18, 4, cGREEN, MEDIUM_FILL); 
  c1.draw();                 // рисование кругов
  c2.draw();
  c3.draw();
  set_cursor_pos(1,25);      // нижний левый угол
  return 0;
} 

Результат работы программы CIRCLES такой же, как и для программы CIRCSTRC (см. рис. 5.5). Возможно, вам будет интересно сравнить две программы. В программе CIRCLES каждый из кругов представлен в виде объекта, а не совокупностью структуры и независимой от нее функции circ_draw(), как это было в программе CIRCSTRC. Обратите внимание, что в программе CIRCLES все, что имеет отношение к кругам, то есть соответствующие данные и функции, объединены в единое целое в определении класса.

Кроме функции draw(), класс circle содержит функцию set(), имеющую пять аргументов и задающую параметры круга. Как мы увидим позже, вместо функции set() лучше использовать конструктор.

Класс как тип данных

Здесь мы рассмотрим пример, демонстрирующий применение объектов C++ в качестве переменных типа, определенного пользователем. Объекты будут представлять расстояния, выраженные в английской системе мер, описанной в главе 4. Ниже приведен листинг программы ENGLOBJ:

// englobj.cpp
// длины в английской системе в качестве объектов
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////
class Distance // длина в английской системе
{
private:
int feet;
float inches;
public:
void setdist( int ft, float in ) // установка значений полей
{ feet = ft; inches = in; }
void getdist() // ввод полей с клавиатуры
{
cout << "\nВведите число футов: "; cin >> feet;
cout << "Введите число дюймов: "; cin >> inches;
}
void showdist() // вывод полей на экран
{ cout << feet << "\'-" << inches << '\"'; }
};
//////////////////////////////////////////////////////////
int main()
{
Distance dist1, dist2; // две длины
dist1.setdist(11,6.25); // установка значений для d1
dist2.getdist(); // ввод значений для dist2
// вывод длин на экран
cout << "\ndist1 = "; dist1.showdist();
cout << "\ndist2 = "; dist2.showdist();
cout << endl;
return 0;
}

В этой программе класс Distance содержит два поля: feet и inches. Он схож со структурой Distance, рассмотренной в главе 4, однако класс Distance имеет три метода: setdist(), предназначенный для задания значений полей объекта через передаваемые ему аргументы, getdist(), получающий эти же значения с клавиатуры, и showdist(), отображающий на экране расстояние в футах и дюймах.

Таким образом, значения полей объекта класса Distance могут быть заданы двумя способами. В функции main() мы определили две переменные типа Distance: dist1 и dist2. Значения полей для первой из них задаются с помощью функции setdist(), вызванной с аргументами 11 и 6.25, а значения полей переменной dist2 вводятся пользователем. Результат работы программы выглядит следующим образом:

Введите число футов: 10

Введите число дюймов: 4.75

dist1 = 11'-6.25"			  - задано аргументами программы
dist1 = 10'-4.75"			  - введено пользователем

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

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

Пример со счетчиком

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

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

// counter.cpp
// счетчик в качестве объекта
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////
class Counter
{

  private:
    unsigned int count;         // значение счетчика
  public:
    Counter() : count(0)        // конструктор
      { /* пустое тело */ }
    void inc_count()            // инкрементирование счетчика
      { count++; }
    int get_count()             // получение значения счетчика
      { return count; }
};

//////////////////////////////////////////////////////////
int main()
{
Counter c1, c2; // определение с инициализацией
cout <<"\nc1=" << c1.get_count(); // вывод
cout <<"\nc2=" << c2.get_count();
c1.inc_count(); // инкрементирование c1
c2.inc_count(); // инкрементирование c2
c2.inc_count(); // инкрементирование c2
cout <<"\nc1="<< c1.get_count(); // вывод
cout <<"\nc2=" << c2.get_count();
cout << endl;
return 0;
}

Класс Counter имеет единственное поле count типа unsigned int, поскольку значение счетчика не может быть отрицательным, и три метода: конструктор Counter(), который мы рассмотрим чуть позже, inc_count(), инкрементирующий поле count, и get_count(), возвращающий текущее значение счетчика.

Начало
Cодержание
Отрывок
[Заказать книгу в магазине "Мистраль"]

 

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...