Литмир - Электронная Библиотека
A
A

struct X {

  int m;

  // ...

};

Он эквивалентен следующему коду:

class X {

public:

  int m;

  // ...

};

Структуры (

struct
) в основном используются для организации данных, члены которых могут принимать любые значения; иначе говоря, мы не можем определить для них никакого осмысленного инварианта (раздел 9.4.3).

9.4. Разработка класса

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

Рассмотрим вполне тривиальную задачу: представить календарную дату (например, 14 августа 1954 года) в программе. Даты нужны во многих программах (для проведения коммерческих операций, описания погодных данных, календаря, рабочих записей, ведомостей и т.д.). Остается только вопрос: как это сделать? 

9.4.1. Структуры и функции

 Как можно представить дату? На этот вопрос большинство людей отвечают: “Указать год, месяц и день месяца”. Это не единственный и далеко не лучший ответ, но для наших целей он вполне подходит. Для начала попробуем создать простую структуру.

// простая структура Date (слишком просто?)

struct Date {

  int y; // год

  int m; // месяц года

  int d; // день месяца

};

Date today; // переменная типа Date (именованный объект)

Объект типа

Date
, например
today
, может просто состоять из трех чисел типа
int
.

Программирование. Принципы и практика использования C++ Исправленное издание - _076.png

В данном случае нет необходимости скрывать данные, на которых основана структура

Date
, — это предположение будет использовано во всех вариантах этой структуры на протяжении всей главы. Итак, теперь у нас есть объекты типа
Date;
что с ними можно делать? Все что угодно, в том смысле, что мы можем получить доступ ко всем членам объекта
today
(и другим объектам типа
Date
), а также читать и записывать их по своему усмотрению. Загвоздка заключается в том, что все это не совсем удобно. Все, что мы хотим делать с объектами типа
Date
, можно выразить через чтение и запись их членов. Рассмотрим пример.

// установить текущую дату 24 декабря 2005 года

today.y = 2005;

today.m = 24;

today.d = 12;

Этот способ утомителен и уязвим для ошибок. Вы заметили ошибку? Все, что является утомительным, уязвимо для ошибок! Например, ответьте, имеет ли смысл следующий код?

Date x;

x.y = –3;

x.m = 13;

x.d = 32;

Вероятно нет, и никто не стал бы писать такую чушь — или стал? А что вы скажете о таком коде?

Date y;

y.y = 2000;

y.m = 2;

y.d = 29;

Был ли двухтысячный год високосным? Вы уверены?

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

Date
к общим операциям относится также увеличение значения объекта
Date
. Итак, напишем следующий код:

// вспомогательные функции:

void init_day(Date& dd, int y, int m, int d)

{

  // проверяет, является ли (y,m,d) правильной датой

  // если да, то инициализирует объект dd

}

void add_day(Date& dd, int n)

{

  // увеличивает объект dd на n дней

}

Попробуем использовать объект типа

Date
.

void f()

{

  Date today;

  init_day(today, 12, 24, 2005); // Ой! (в 12-м году не было

                                 // 2005-го дня)

  add_day(today,1);

}

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Во-первых, отметим полезность таких “операций” — здесь они реализованы в виде вспомогательных функций. Проверка корректности даты довольно сложна и утомительна, поэтому, если бы мы не написали соответствующую функцию раз и навсегда, то скорее всего пропустили бы этот код и получили неправильную программу. Если мы определяем тип, то всегда хотим выполнять над его объектами какие-то операции. Точное количество и вид этих операций может изменяться. Точный вид реализации этих операций (в виде функций, функций-членов или операторов) также изменяется, но как только мы решили создать собственный тип, мы должны спросить себя: “Какие операции с этим типом можно выполнять?”

9.4.2. Функции-члены и конструкторы

Мы предусмотрели функцию инициализации для типа

Date
, которая проверяет корректность его объектов. Однако функции проверки приносят мало пользы, если мы не можем их использовать. Например, допустим, что мы определили для типа
Date
оператор вывода
<<
(раздел 9.8):

125
{"b":"847443","o":1}