Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

hkjCJgcQqF

.pdf
Скачиваний:
2
Добавлен:
15.04.2023
Размер:
810.94 Кб
Скачать

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

Контрольные вопросы

1.Истинно ли следующее утверждение: поля класса должны быть закрытыми?

2.Имя конструктора совпадает с именем __________ ?

3.Константный метод, вызванный для объекта класса:

может изменять как неконстантные, так и константные поля; может изменять только неконстантные поля; может изменять только константные поля;

не может изменять как неконстантные, так и константные поля.

4. Истинно ли следующее утверждение: объект, объявленный как константный, можно использовать только с помощью константных методов?

Задание для самостоятельной работы Тема: Создание и использование классов

Цель: Разработать и использовать класс случайных чисел Краткие теоретические сведения

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

33

ся класс RandomNumber. В соответствии с начальным seed-значением генератор создает псевдослучайную последовательность. Класс обеспечивает автоматический выбор seed-значения, когда конструктору не передается никакого значения, и позволяет клиенту создавать независимые псевдослучайные последовательности.

Спецификация класса RandomNumber Объявление

#ifndef RANDOM_NUMBER_GENERATOR #define RANDOM_NUMBER_GENERATOR #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. Функция fRandom возвращает число с плавающей точкой в диапазоне от 0 до 1.0

Пример RandomNumber rnd;

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

34

//создает последовательность с seed-значением пользователя 1 cout << R.fRandom ( );

//выводит действительное число в диапазоне 0-1 // выводит 5 случайных целых чисел в диапазоне 0 – 99 for (int i=0; i<5; i++)

cout << R.Random (100 ) << “ “;

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

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

RandomNumber die; //использует автоматич. seeding dicevalue = die.Random (6 )+1;

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

RandomNumber FNum;

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

value = FNum. fRandom ( ) * 25 + 50; //умножение на 25 ; прибавление 50 Реализация класса RandomNumber

Для создания псевдослучайных чисел используется линейный конгруэнтный алгоритм. Этот алгоритм использует большой нечетный постоянный множитель и постоянное слагаемое вместе с seed-значением для итеративного создания случайных чисел и обновления seed-значения:

const unsigned long maxshort = 65536L; const unsigned long multiplier = 1194211693L; const unsigned long adder = 12345L;

Последовательность случайных чисел начинается с начального значения для длинного целого randSeed. Задание этого значения называется настройкой генератора случайных чисел и выполняется конструктором.

Конструктор позволяет клиенту передавать seed-значение или использовать для его получения машинно-зависимую функцию time. При вызове конструктора с параметром 0 функция time возвращает беззнаковое длинное (32 –битовое ) число, указывая количество секунд, прошедших после базового времени.

// генерация seed-значения RandomNumber::RandomNumber (unsigned long s)

{

35

if (s == 0)

 

randSeed = time(0); // использование системной функции time

else

 

randSeed = s;

// пользовательское seed-значение

}

 

В каждой итерации

используются константы для создания нового

беззнакового длинного seed-значения:

randSeed = multiplier * randSeed + adder;

В результате умножения и сложения верхние 16 битов 32-битового значения randSeed являются случайными (“хорошо перемешанными”) числами. Используемый алгоритм создает случайное число в диапазоне от 0 до n-1, беря остаток от деления на n. Результатом является значение Random (n)

// возвращать случайное целое 0 <= value <= n-1 < 65536 unsigned short RandomNumber::Random (unsigned long n)

{

randSeed = multiplier * randSeed + adder; return (unsigned short)((randSeed >> 16) % n);

}

Для числа с плавающей точкой сначала вызываем метод Random ( maxshort ), который возвращает следующее случайное целое число в диапазоне от 0 до maxshort-1. После деления на double (maxshort ) получаем действительное число в интервале

0 ≤ fRandom ( ) < 1.0 .

// возврат числа 0..65535) / 65536 double RandomNumber::fRandom ( )

{

return Random(maxshort)/double(maxshort);

}

Задание

№ 1. Создайте приложение, которое выводит график частоты выпадения лицевой стороны при бросании монет. Класс RandomNumber нужно использовать для имитации повторяемого бросания некоторого количества монет (10 или 20). Бросание монет имеет результатом число падений лицевой стороной в диапазоне от 0 до количества монет. Бросание монет составляет событие. Метод Random с параметром 2 моделирует одно бросание монеты, а возвращаемое значение 1 – как лицевые стороны. Рекомендуется в функции, подсчитывающей общее количество выпадений лицевой стороны, объявить статический объект типа RandomNumber.

36

Результат должен быть представлен, например, так как показано на рисунке.

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

• Одна пятая часть автомобилей области не соответствует стандартам по вредным эмиссиям. Используйте fRandom, чтобы определить , отвечает ли этим стандартам случайно выбранная машина.

• Вес особи в популяции варьируется в пределах 48150 кг. Используйте Random для выбора веса какого-либо человека в этой популяции.

№ 3. Следующий код является объявлением для класса, представляющего колоду карт:

class CardDesk { private:

//колода карт реализуется как массив целых от 0 до 51 int cards[52];

int currentCard; public:

//конструктор . тасование колоды карт CardDesk(void);

void Shuffle(void);

// возвращать следующую карту в колоде. Указание: нужно установить текущее положение в колоде

int GetCard(void);

//трефы 0-12., бубны 13-25., черви 2638., пики 39-51. В каждом диапазоне первая карта –это туз, а последние три картыэто валет, дама, король. Запишите карту как масть и значение карты

void PrintCard(int c); };

Реализуйте эти методы. Указание: Для тасования карт используйте цикл со сканированием 52-карт. Для карты i выберите случайное число в диапазоне

37

от i до 51 и поменяйте местами карту с этим случайным индексом и карту с индексом i.

Запишите функцию

void DealHand(CardDesk& d, int n);

которая сдает n карт из d , сортирует их и печатает их значения.

№ 4. Используйте класс CardDesk, для карточной игры под названием “Веришь -не веришь“. Сдайте пять карт. Для каждой карты спросите игрока, будет ли случайно вытащенная карта из оставшихся карт в колоде больше или меньше данной карты. Туз –самая большая карта любой масти, и масти упорядочены от трефовой до пиковой . Печатайте количество удачных догадок.

Контрольные вопросы

1.Константный метод, вызванный для объекта класса:

может изменять как неконстантные, так и константные поля;

может изменять только неконстантные поля;

может изменять только константные поля;

не может изменять как неконстантные, так и константные поля.

2.Истинно ли следующее утверждение:

объект, объявленный как константный, можно использовать только с помощью константных методов?

3.В определении класса члены класса с ключевым словом private доступны:

любой функции программы;

в случае, если известен пароль; методам этого класса;

только открытым членам класса.

4.Истинно ли следующее утверждение:

поля класса должны быть закрытыми?

38

Глава 2. Перегруженные функции

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

int min ( int, int ) ;

int min ( const vector<int> & ) ; int min ( const matrix & ) ;

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

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

int min ( int a, int b ) ; int min ( int a, int b=7 ) ;

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

Квалификаторы const и volatile при сравнении списков параметров во внимание не принимаются. Так следующие два объявления считаются одинаковыми (и ошибочными):

void func (int ); void func (const int );

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

Например, объявляются разные функции: void func ( int * );

void func (const int *);

и здесь объявляются разные функции: void func ( int & );

void func (const int &);

39

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

Все перегруженные функции объявляются в одной и той же области видимости. Локально объявленная функция не перегружает, а просто затеняет глобальную. Поскольку каждый класс определяет собственную область видимости, то функции, являющиеся членами двух разных классов, не перегружают друг друга. Объявлять перегруженные функции разрешается и внутри пространства имен. С каждым пространством имен связана отдельная область видимости, так что функции, объявленные в разных пространствах не перегружают друг друга. Использование usingобъявлений и using-директив помогает сделать члены пространства имен доступными в других областях видимости. Например:

namespace MyNameSpace { int min ( int, int );

double min ( double, double );

}

//using-объявление

using MyNameSpace::min; void func ()

{

min( 57, 41);

min ( 45.6 , 67,9 );

}

Using-объявление вводит обе функции MyNameSpace::min в глобальную область видимости. По типам аргументов определяется, какую именно функцию вызывать.

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

namespace ABC {

 

int func ( int );

 

}

 

namespace BCD {

 

double func (double );

 

}

 

using namespace ABC;

// using-директива

using namespace BCD;

//формируется множество перегруженных

функций

 

char * func (char * );

//из различных пространств времен

int main ( )

 

40

{

func (4); // вызывается ABC::func (int) func (8.97); //вызывается BCD::func ( double ) return 0;

}

Директива extern “C” и перегруженные функции

Директиву линкования extern “C” можно использовать в программе на C++ для того, чтобы указать, что некоторый объект находится в части, написанной на языке С. В директиве линкования разрешается задать только одну из множества перегруженных функций:

//ошибка : для двух перегруженных функций указана // директива линкования extern “C”

extern “C” void func (const char * ); extern “C” void func (double) ;

Директива линкования не имеет значения при решении, какую функцию вызывать ; важны только типы параметров. Выбирается та

функция, которая

лучше всего соответствует типам переданных

аргументов.

 

Указатели на перегруженные функции

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

int min ( int, int ) ;

double min ( const vector<double> & ) ; int min ( const matrix & ) ;

И объявлен указатель на функцию: int ( *pf1 ) ( int , int ) = & min;

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

Шаги разрешения перегрузки

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

При разрешении перегрузки функции выполняются следующие

шаги:

выделяется множество перегруженных функций для данного вызова, а также распознаются свойства списка аргументов, переданных функции;

41

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

находится функция, которая лучше всего соответствует вызову.

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

void myfunc( ); void myfunc( int );

void myfunc( double, double = 3.14 ); void myfunc( char *, char * );

int main ( )

{

myfunc (5.7) ; return 0;

}

В этом примере есть четыре кандидата: myfunc( ) , myfunc( int ),

myfunc(

double,

double = 3.14 ),

myfunc( char *, char * ).

Список

аргументов состоит из одного аргумента типа double.

 

На

втором

шаге

среди

множества кандидатов отбираются

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

функция myfunc(int) подходит, потому что у нее есть всего один параметр и существует преобразование фактического аргумента типа double к формальному параметру типа int;

функция myfunc(double,double) подходит, потому что для второго аргумента есть значение по умолчанию, а первый формальный параметр имеет тип double, что в точности соответствует типу фактического аргумента.

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

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

42

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]