Реализация класса Temperature

Пользовательский тип — КЛАСС

private: данные операторы
Класс — это определяемый пользователем тип с данными и функциями (методами), называемыми членами (members) класса. Переменная типа класс называется объект (object). Класс создает различные уровни доступа к его членам, разделяя объявление на части: private, protected и public. Часть private (закрытая) объекта может быть доступна только для функций-членов в этом классе. Часть public (открытая) объекта может быть доступна для внешних элементов программы, в области действия которых находится этот объект (рис. 3.1). Protected (защищенные) члены используются с производными классами и описываются в главе, посвященной наследованию.

 

 
 

 

 


Рис. 1.1. Доступ к методам класса

Объявление класса

Объявление класса начинается с заголовка класса (class head), состоящего из зарезервированного слова class, за которым следует имя класса. Члены класса определяются в теле класса (class body), которое заключается в фигурные скобки и заканчивается точкой с запятой. Зарезервированные слова public и private разделяют члены класса, и эти спецификаторы доступа заканчиваются двоеточием. Члены класса объявляются как переменные C++, а методы задаются как объявления функций C++. Общая форма объявления класса такова:

class Class_Name

{

private: //Закрытые данные

//Объявление закрытых методов

//

public: //Открытые данные

//Объявление открытых методов

//

};

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

Пример 1.

Класс Rectangle

При геометрических измерениях прямоугольник определяется его длиной и шириной. Это позволяет нам вычислять периметр и площадь фигуры. Параметры длины и ширины и операции объединяются для образования абстрактного типа данных прямоугольной фигуры. Вы должны самостоятельно подготовить спецификацию ADT в качестве упражнения, освоив самостоятельно главу 1, а здесь будет разработан класс Rectangle C++, который реализует этот ADT. Класс содержит конструктор и набор методов — GetLength, PutLength, GetWidth и PutWidth, имеющих доступ к закрытым членам класса. Объявление класса Rectangle следующее:

class Rectangle

{

private:

//длина и ширина прямоугольного объекта

float length, width

public:

//конструктор

Rectangle(float l=0, float w=0);

//методы для нахождения и изменения закрытых данных

float GetLength(void) const;

void PutLength(float 1);

float GetWidth(void) const;

void PutWidth(float w);

//вычислять и возвращать измерения прямоугольника

float Perimeter(void) const;

float Area(void) const; I

}

Обратите внимание, что методы GetLength, GetWidth, Perimeter и Area имеют ключевое слово const после списка параметров. Это объявляет каждый метод как константный. В определении константного метода никакой элемент данных не может быть изменен. Иначе, выполнение метода, объявленного как const, не изменяет состояния объекта Rectangle.

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

Конструктор

Функция, называемая конструктором (constructor) класса, имеет то же имя, что и класс. Подобно другим функциям C++, конструктору могут передаваться параметры, используемые для инициализации одного или более данных-членов класса. В классе Rectangle конструктору дается имя Rectangle, и он принимает параметры l и w, используемые для инициализации длины и ширины объекта, соответственно. Заметьте, что эти параметры имеют значения по умолчанию, которые указывают, что используется значение 0, когда параметр l или w не передается явно.

Пример 1 иллюстрирует объявление класса (class definition), так как методы описываются только объявлениями функций. Код C++ для определения отдельных функций создает реализацию класса (class implementation).

Объявление объекта

Объявление класса описывает новый тип данных. Объявление объекта типа класс создает экземпляр (instance) класса. Это делает реальным объект типа класс и автоматически вызывает конструктор для инициализации некоторых или всех данных-членов класса. Параметры для объекта передаются конструктору заключением их в скобки после имени объекта. Заметьте, что конструктор не имеет возвращаемого типа, поскольку вызывается только во время создания объекта:

ClassName object(<parameters>);//список параметров может быть

// пустым

Например, следующие объявления создают два объекта типа Rectangle:

Rectangle room(12, 10);

Rectangle t; //использование параметров по умолчанию (0, 0).

Каждый объект имеет полный диапазон данных-членов и методов, объявляемых в классе. Открытые члены доступны с использованием имени объекта и имени члена, разделяемых «.» (точкой). Например:

х = room.Area(); //присваивает х площадь 12*10=120

t.PutLength(20); //присваивает 20 как длину объекта Rectangle

// Текущая длина равна 0 так как используются параметры

// по умолчанию,

cout<<t.GetWidth(); // выводит текущую ширину, которая = 0

// по умолчанию

В объявлении объекта Room конструктор первоначально устанавливает значение длины, равным 12, а ширины — 10. Клиент может изменять размеры, используя методы доступа PutLength и PutWidth:

room.PutLength(15); //изменение длины и ширины на 15 и 12

room.PutWidth(12);

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

Класс Rectangle содержит члены класса типа float. В общем, класс может содержать элементы любого допустимого типа C++, даже других классов. Однако, класс не может содержать объект его собственного типа в качестве члена.

Реализация класса

Каждый метод в объявлении класса должен быть определен. Определения функций могут быть заданы в теле класса (встроенный код) или вне его. При помещений функций вне тела имя класса, за которым следует два двоеточия, должно предшествовать имени этой функции. Символ "::" называется операцией разрешения области действия (scope resolution operator) и указывает на то, что функция принадлежит области действия класса. Это позволяет всем операторам в определении функции иметь доступ к закрытым членам класса. В случае с классом Rectangle идентификатор "Rectangle::" предшествует именам методов.

Далее следует определение GetLength(), когда она записана вне тела класса Rectangle:

float Regtangle::GetLength(void) const

{

return length; //доступ к закрытому члену length

}

Заметьте, что при определении константного метода может быть также использован квалификатор const.

Функция-член класса может быть записана внутри тела класса. В этом случае код является расширенным встраиваемым (expanded inline), а операция разрешения области действия не используется, так как код находится в области действия тела класса. Встраиваемое определение операции GetLength имеет вид:

class Rectangle

{

private:

float length;

float width;

public:

float GetLength(void) const //код задается как inline

{

return(length);

}

};

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

Реализация конструктора

Конструктор может быть определен как inline или вне тела класса. Например, следующий код определяет конструктор Rectangle:

Rectangle::Rectangle(float 1, float w)

{

length = l;

width = w;

}

C++ предоставляет специальный синтаксис для инициализации членов класса. Список инициализации членов (member initialization list) — это список имен данных-членов класса, разделенных запятыми, за каждым из которых следует начальное его значение, заключенное в скобки. Начальные значения обычно являются параметрами конструктора, которые присваиваются соответствующим данным-членам класса в списке. Список инициализации членов помещается после заголовка функции и отделяется от списка параметров двоеточием:

 

ClassName::ClassName(parm list):data1(parm1),..., datan(parmn)

Например, параметры конструктора l и w могут быть присвоены данным-членам класса length и width:

Rectangle::Rectangle(float l, float w): length(l), width(w)

{}

Создание объектов

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

Rectangle square(10, 10), yard = square, S;

Объект square создается с length и width, равными 10. Второй объект yard создается с начальными данными, копируемыми из объекта square. Объект S имеет length и width, по умолчанию равными 0.

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

S = yard;

копирует все данные из объекта yard в объект S. В этом случае length и width объекта yard копируются в length и width объекта S.

Объект может быть создан ссылкой на его конструктор. Например, объявление Rectangle(10,5) создает временный объект с lengh = 10 и width = 5. В следующем операторе операция присваивания копирует данные из временного объекта в rectangle S:

S = Rectangle(10,5);

Пример 2.

1.Операторы

S = Rectangle(10,5);

cout << S.Area() << endl;

приводят к выводу в поток cout числа 50.

2.Оператор

cout << Rectangle(10,5).GetWidth() << endl;

Выводит число 5.

 

Программа 1. Использование класса Rectangle

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

Предположим, что опалубка проходит по периметру передней стороны и периметру проема двери. Мы запрашиваем у пользователя размер передней стороны гаража и затем вводим цикл, позволяющий выбрать размер двери. Цикл заканчивается, когда пользователем выбирается опция "Quit". Для каждого выбора двери программа определяет стоимость отделки передней стороны гаража и выводит это значение. Мы задаем константами стоимость деревянной обшивки $2 за кв. фут и стоимость опалубки на $0,50 за погонный фут.

 
 


Опалубка

 
 


Дверь

 

Обшивка

 

Длина опалубки равна сумме периметров передней стороны гаража и двери. Стоимость обшивки равна площади передней стороны гаража минус площадь двери.

//Example1.срр

#include <iostream.h>

class Rectangle

{

private:

// длина и ширина прямоугольного объекта

float length, width;

public:

// конструктор

Rectangle(float l=0, float w = 0);

// методы для получения и модификации закрытых данных

float GetLength(void) const;

void PutLength(float 1);

float GetWidth (void) const;

void PutWidthffloat w) ;

//вычисление характеристик прямоугольника

float Perimeter (void) const;

float Area(void) const;

}

//конструктор, выполняет присваивания: length=l, width=w

Rectangle:: Rectangle(float l, float w): length(l), width(w)

{}

// возвратить длину прямоугольника

float Rectangle:: GetLength(void) const

{

return length;

}

//изменить длину прямоугольника

void Rectangle:: PutLength(float l)

{

length = l;

}

// возвратить ширину прямоугольника

float Rectangle:: GetWidth(void) const

{

return width;

}

// изменить ширину прямоугольника

void Rectangle:: PutWidth(float w)

{

width = w;

}

// вычислить и возвратить периметр прямоугольника

float Rectangle:: Perimeter(void) const

{

return 2.0*(length + width) ;

}

//вычислить и возвратить площадь прямоугольника

float Rectangle:: Area(void) const

{

return length*width;

}

void main(void)

{

//стоимости обшивки и опалубки - постоянные

const float sidingCost = 2.00, moldingCost = 0.50;

int completedSelections = 0;

//опция из меню, выделенная пользователем

char doorOption;

//длина/ширина и стоимость двери

float glength, gwidth, doorCost;

//общая стоимость, включая дверь, обшивку и опалубку

float totalCost;

cout << "Введите длину и ширину гаража: ";

cin >> glength >> gwidth;

//создать объект garage(гараж) с размерами по умолчанию

// создать объект door(дверь) с размерами по умолчанию

Rectangle garage(glength, gwidth);

Rectangle door;

while (!completedSelections)

{

cout << "Введите 1-4 или 'q' для выхода" << endl << endl;

cout << "Дверь 1 (12х8; $380) "

cout << "Дверь 2 (12x10; $420) "<<endl;

cout << "Дверь 3 (16x8; $450) "

cout << "Дверь 4 (16 x10; $480)" << endl;

cout << endl;

cin >> doorOption;

if(doorOption == 'q')

completedSelections = 1;

else

switch (doorOption)

{

case '1':door.PutLength(12); // 12 x 8 ($380)

door.PutWidth(8) ;

doorCost = 380;

break;

case '2':door.PutLength (12) ; // 12 x 10 ($420)

door.PutWidth(10);

doorCost = 420;

break;

case '3':door.PutLength (16) ; // 16 x 8 ($450)

door.PutWidth(8);

doorCost = 450;

break;

case '4':door.PutLength (12) ; //16x10 ($480)

door.PutWidth(10);

doorCost = 480;

break;

}

totalCost=doorCost+moldingCost*(garage.Perimeter()+

door.Perimeter())+sidingCost* (garage.Area()-door.Area());

cout <<"Общая стоимость двери, обшивки и опалубки:$" <<

totalCost << endl << endl;

}

}

}

/*

<Запуск программы 3.1>

Введите длину и ширину гаража: Введите 1-4 или 'q' для выхода

Дверь 1 (12х8; $380) Дверь 2 (12х10; $420)

Дверь 3 (16х8; $450) Дверь 4 (16х10; $480)

Общая стоимость двери, обшивки и опалубки: $720

Введите 1-4 или ' q' для выхода

Дверь 1 (12х8; $380) Дверь 2 (12х10; $420)

Дверь 3 (16x8; $450) Дверь 4 (16x10; $480)

q

*/

Примеры классов

Следующие два примера классов иллюстрируют конструкторы класса в С++. Класс Temperature поддерживает записи значений высокой и низкой температуры. В качестве приложения объект мог бы иметь высокую (точка кипения) и низкую (точка замерзания) температуры воды. ADT RandomNumber определяет тип для создания последовательности целых или с плавающей точкой случайных чисел. В реализации C++ конструктор позволяет клиенту самому инициализировать последовательность случайных чисел или использовать программный способ получения последовательности с системно-зависимой функцией времени.

Класс Temperature

Класс Temperature содержит информацию о значениях высокой и низкой температуры. Конструктор присваивает начальные значения двум закрытым данным-членам highTemp и lowTemp, которые являются числами с плавающей точкой. Метод UpdateTemp принимает новое значение данных и определяет, должно ли обновляться одно из значений температуры в объекте. Если отмечается новое самое низкое значение, то обновляется lowTemp. Аналогично, новое самое высокое значение изменит highTemp. Этот класс имеет два метода доступа к данным: GetHighTemp возвращает самую высокую температуру, a GetLowTemp возвращает самую низкую температуру.

Спецификация класса Temperature

ОБЪЯВЛЕНИЕ

class Temperature

{

private:

float highTemp, lowTemp; // закрытые данные-члены

public:

Temperature(float h, float l);

void UpdateTemp(float temp);

float GetHighTemp(void) const;

float GetLowTemp(void) const;

};

ОБСУЖДЕНИЕ

Конструктору должны быть переданы начальные высокая и низкая температуры для объекта. Эти значения могут быть изменены методом UpdateTemp. Методы GetLowTemp и GetHighTemp являются константными функциями, так как они не изменяют никакие данные-члены в классе.

ПРИМЕР

//точка кипения/замерзания воды по Фаренгейту

Temperature fwater(212,32);

//точка кипения/замерзания воды по Цельсию

Temperature cwater(100, 0) ;

cout << Вода замерзает при<<cwater.GetLowtemp << " С"<< endl;

cout << Вода кипит при << fwater.GetHighTemp << " F" << endl;

Выход:

Вода замерзает при 0 С

Вода кипит при 212 F

Реализация класса Temperature

Каждый метод в классе записывается вне тела класса с использованием оператора области действия. Конструктор принимает начальные показания высокой и низкой температуры, которые присваиваются полям highTemp и lowTemp. Эти значения могут изменяться только методом UpdateTemp, когда новая высокая или низкая температура передаются в качестве параметра. Функции доступа GetHighTemp и GetLowTemp возвращают значение высокой и низкой температуры.

//Конструктор: присвоить данные: highTemp=h и lowTemp=l

Temperature::Temperature(float h, float l): highTemp(h),

lowTemp(l)

{}

//обновление текущих показаний температуры

void Temperature::UpdateTemp (float temp)

{

if (temp>highTemp)

highTemp = temp;

else if (temp<lowTemp)

lowTemp = temp;

}

//возвратить high (самая высокая температура)

float Temperature::GetHighTemp (void) const

{

return highTemp;

}

//возвратить low (самая низкая температура)

float Temperature::GetLowTemp (void) const

{

return lowTemp;

}

 

Программа 2. Использование класса Temperature

// Example2.cpp

#include <iostream.h>

#include "temp.h"

void main (void)

{

Temperature today (70,50);

float temp;

cout << "Введите температуру в полдень: ";

cin >> temp;

// обновить объект для включения дневной температуры

today.UpdateTemp (temp) ;

cout << "В полдень: Наивысшая :" << today.GetHighTemp ();

cout << " Низшая " << today.GetLowTemp ()<<endl;

cout << "Введите вечернюю температуру: ";

cin >> temp;

//обновить объект для включения вечерней температуры

today.UpdateTemp (temp) ;

cout << "Сегодня наивысшая :" << today.GetHighTemp();

cout << " Низшая " << today.GetLowTemp() << endl;

}

/*

<3апуск программы pr03_02.cpp>

Введите температуру в полдень: 80

В полдень: Наивысшая :80 Низшая 50

Введите вечернюю температуру: 40

Сегодня наивысшая :80 Низшая 40

*/

 

Класс случайных чисел

Для многих приложений требуются случайные данные, представляющие случайные события. Моделирование самолета, тестирующее реакцию летчика на непредвиденные изменения в поведении самолета, карточная игра, предполагающая, что сдающий использует тасованную колоду, и изучение сбыта, предполагающее вариации в прибытии клиентов, – все это примеры компьютерных приложений, которые опираются на случайные данные. Компьютер использует генератор случайных чисел (random number generator), который выдает числа в фиксированном диапазоне таким образом, что числа равномерно распределяются в этом диапазоне. Генератор использует детерминистический алгоритм, который начинается с нача­льного значения данных, называемого значением, инициализирующим алгоритм, или seed-значением. Алгоритм манипулирует этим значением для генерирования последовательности чисел. Этот процесс является детерминистическим, так как он берет начальное значение и выполняет фиксированный набор инструкций. Выход является уникальным, определенным данными и инструкциями. По существу, компьютер не производит истинные случайные числа, а создает последовательности псевдослучайных чисел (pseudorandom numbers), которые распределяются равномерно в диапазоне. Вследствие начальной зависимости от seed-значения, генератор создает ту же последовательность при использовании одного и того же seed-значения. Способность повторять случайную последовательность используется в исследованиях моделирования, где в приложении необходимо сравнить различные стратегии, реагирующие на один и тот же набор случайных условий. Например, имитатор полета использует одну и ту же последовательность случайных чисел для сравнения эффективности реакции двух летчиков на аварию самолета. Каждый летчик подвергается одному и тому же набору событий. Однако, если seed-значение изменяется каждый раз при запуске имитатора, мы имеем уникальное моделирование. Эта уникальность свойственна игре, которая обычно создает различную последовательность событий каждый раз в процессе игры.

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

Спецификация класса RandomNumber

ОБЪЯВЛЕНИЕ

#include <time.h>

// используется для генерации случайного числа

//по текущему seed-значению

const unsigned long maxshort = 65536L;

const unsigned long multiplier = 1194211693L;

const unsigned long adder = 12345L;

class RandomNumber

private:

// закрытый член класса, содержащий текущее seed-значение

unsigned long randSeed;

public:

//конструктор, параметр 0 задает автоматический

// выбор seed-значения

RandomNumber(unsigned long s = 0);

// генерировать случайное целое в диапазоне [0, n-1]

unsigned short Random(unsigned long n);

// генерировать действительное число в диапазоне [0, 1.0]

double fRandom(void);

};

ОПИСАНИЕ

Начальное seed-значение – это беззнаковое длинное число. Метод Random принимает беззнаковый длинный параметр n <= 65536 и возвращает 16-битовое беззнаковое короткое значение в диапазоне 0,..., n–1. Заметьте, что если возвращаемое методом Random значение присваивается целой переменной со знаком, то это значение может интерпретироваться как отрицательное, если n не будет удовлетворять неравенству n<215=32768. Функция fRandom возвращает число с плавающей точкой в диапазоне 0 < fRandom() < 1.0.

ПРИМЕР

RandomNumber rnd; // seed-значение выбирается автоматически

RandomNumber R(1); // создает последовательность с

// seed пользователя 1

cout << R.fRandom(); // выводит действительное число

// в диапазоне 0—1

//выводит 5 случайных целых чисел в диапазоне 0—99

for (int i = 0; i < 5; i++)

cout << R.Random(100) << " "; // <sample> 93 21 45 5 3

Пример 3.

Создание случайных данных

1.Значение грани кости находится в диапазоне 1¸6 (шесть вариантов). Для имитации бросания кости используйте функцию die.Random(6), которая возвращает значения в диапазоне 0¸5. Затем прибавьте 1 для перевода случайного числа в нужный диапазон.

RandomNumber Die //использует автоматически seeding

dicevalue = die.Random(6) +1;

2.Объект FNum использует автоматическое задание seed-значения для создания случайной последовательности:

RandomNumber FNum;

Для вычисления плавающего значения в диапазоне 50<=х<75 генерируйте случайное число в диапазоне 0¸25, умножая результат f Random на 25. Это расширяет диапазон случайных чисел от 1-й единицы (0<х<1) до 25 единиц (0<х<25). Преобразуйте нижнюю границу нового диапазона, добавив 50:

value=FNum.fRandom()*25+50; //умножение на 25;прибавление 50