Изброен тип и логически тип
(enum and bool)
В тази лекция ще се занимаем с два типа - изброен тип и логически тип. Те обикновенно се разглеждат заедно в книгите по програмиране, защото логическият тип е един вид предефиниран изброен тип.
1. Специфициране на изброен тип (Specifying an Enumerated Type). Спецификацията започва с ключовата дума enum, последвана от името на типа и заградени в големи скоби имената на стойностите на типа, които са разделени със запетайки. Накрая спецификацията завършва с точка и запетая. Едни от най-разпространените примери са дните от седмицата и боите на картите за игра:
enum days_of_week {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
enum Suit {clubs, diamonds, hearts, spades};
Типът се нарича изброен, защото всички стойности, които може да приема променлива от този тип, се изброяват в големите скоби. Т.е. променлива от тип Suit може да приема четирите стойности clubs,diamonds,hearts и spades. Тези четири имена не са имена на променливи! Те не могат да стоят от лявата страна на едно присвояване! Тези четири имена могат да се разглеждат като константи от целочислен тип, както ще бъде показано по-долу.
2. Създаване и използване на променливи от изброен тип (Creating and Using Enumerated Variables). Дефинирането на такива променливи от тип enum е като с всеки един тип в C++ - първо се изписва името на типа и след него се изписват имената на променливите, разделени със запетайка. Следният код дефинира няколко променливи от гореспецифицираните два типа.
days_of_week yesterday, today, tomorrow;
Suit suit_of_card1, suit_of_chosen_card;
След дефиниране на тези променливи те могат да се използват както всички променливи в C++.
yesterday
= Sun;
today
= Mon;
tomorrow
= Tue;
if
(yesterday == Thu)
tomorrow = Sat;
suit_of_chosen_card
= diamonds;
suit_of_card1
= spades;
Разбира се, не можем да присвояваме други стойности на променливите, например следният код е невалиден
enum
days_of_week {Sun, Mon, Tue, Wed, Thu, Fri, Sat }; // It is OK!
days_of_week
yesterday, today, tomorrow;
// It is OK!
yesterday
= Easter
// It is NOT OK! What is Easter?
Mon
= yesterday;
// ILLEGAL: Mon is a constant
2. Изброеният тип е целочислен тип. Нека разгледаме кода по-долу.
enum
days_of_week {Sun, Mon, Tue, Wed, Thu, Fri, Sat };
cout
<< "Value of Sun = " << Sun << endl
<< "Value of Mon = " << Mon << endl
<< "Value of Tue = " << Tue << endl
<< "Value of Wed = " << Wed << endl
<< "Value of Thu = " << Thu << endl
<< "Value of Fri = " << Fri << endl
<< "Value of Sat = " << Sat << endl;
На екрана той дава следното:
Value
of Sun = 0
Value
of Mon = 1
Value
of Tue = 2
Value
of Wed = 3
Value
of Thu = 4
Value
of Fri = 5
Value
of Sat = 6
Т.е. на първата стойност в средните скоби при спецификацията на изброеният тип days_of_week съответства нула, на втората единица и т.н. Тези стойности могат да се разглеждат като целочислени и за тях важат същите операции както и с целочисления тип. Но да не забравяме, че те не са променливи и не могат да стоят в лявата част на едно присвояване. Разгледайте следните програмни редове:
enum days_of_week {Sun, Mon, Tue, Wed, Thu, Fri, Sat };
days_of_week
yesterday, today, tomorrow;
today
= Mon;
yesterday
= today - 1;
tomorrow
= today + 1;
cout
<< "yesterday = " << yesterday << endl
<< "today = " << today << endl
<< "tomorrow = " << tomorrow << endl;
Те ще дадат следния изход на екрана
yesterday
= 0
today
= 1
tomorrow
= 2
Целите числа, свързани със стойностите на типа могат да бъдат променени. Например следният код
enum
days_of_week {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat };
cout
<< "Value of Sun = " << Sun << endl
<< "Value of Mon = " << Mon << endl
<< "Value of Tue = " << Tue << endl
<< "Value of Wed = " << Wed << endl
<< "Value of Thu = " << Thu << endl
<< "Value of Fri = " << Fri << endl
<< "Value of Sat = " << Sat << endl;
if
(Sun < Mon)
// logical comparisson
cout << "It is American day-of-the-week count!";
else
cout << "It is Bulgarian day-of-the-week count!";
присвоява 7 на Sun, 1 на Mon, 2 на Tue и т.н. до 6 на Sat. Ето и изходът на екрана:
Value
of Sun = 7
Value
of Mon = 1
Value
of Tue = 2
Value
of Wed = 3
Value
of Thu = 4
Value
of Fri = 5
Value
of Sat = 6
It
is Bulgarian day-of-the-week count!
Забележете, че стойностите на Tue и т.н. до Sat не се указват изрично, а се подразбират от Mon = 1, че започват от 2 и нарастват с единица. Бъдете внимателни с тези стойности, защото например следният код (подобен на няколко от редовете по-горе) дава неочаквани резултати, в смисъл на получаване на целочислена стойност (в случая 1), която я няма сред стойностите на типа days_of_week.
enum days_of_week {Sun, Mon = 2, Tue, Wed, Thu, Fri, Sat };
days_of_week
yesterday, today, tomorrow;
today
= Mon;
//
yesterday obtains a value of 1
//
which not among the values of days_of_week!!!
yesterday
= today - 1;
tomorrow
= today + 1;
cout
<< "yesterday = " << yesterday << endl
<< "today = " << today << endl
<< "tomorrow = " << tomorrow << endl;
4. Две примерни програми, които боравят с изброен тип. И двете програми са взети от превъзходния курс на Лафоор [1], Първата програма моделира разместването на три карти и познаването на една от тях. В програмата се специфицира структура card, която моделира карта за игра. Дефинират се шест карти от типа card. Една от тях, temp, се използва при размяната на картите, другата, chosen, е избраната от нас, трета, prize, е тази, за която ще спечелим, а останалите три, card1, card2 и card3, са конкретни карти. Обърнете внимание как се разменят картите, например първа и втора: temp = card2; card2 = card1; card1 = temp;: това е много често срещана ситуация в програмирането, например при сортиране на елементите на масив.
//
cardenum.cpp
//
demonstrates enumerations
#include
<iostream>
#include
"conio.h"
using
namespace std;
const
int jack = 11; //2 through
10 are unnamed integers
const
int queen = 12;
const
int king = 13;
const
int ace = 14;
enum
Suit { clubs, diamonds, hearts, spades };
////////////////////////////////////////////////////////////////
struct
card
{
int number;
//2 to 10, jack, queen, king, ace
Suit suit;
//clubs, diamonds, hearts, spades
};
////////////////////////////////////////////////////////////////
int
main()
{
card temp, chosen, prize;
//define cards
int position;
card card1 = { 7, clubs };
//initialize card1
cout << "Card 1 is the seven of clubs\n";
card card2 = { jack, hearts };
//initialize card2
cout << "Card 2 is the jack of hearts\n";
card card3 = { ace, spades };
//initialize card3
cout << "Card 3 is the ace of spades\n";
prize = card3; //copy this card, to remember it
cout << "I'm swapping card 1 and card 3\n";
temp = card3; card3 = card1; card1 = temp;
cout << "I'm swapping card 2 and card 3\n";
temp = card3; card3 = card2; card2 = temp;
cout << "I'm swapping card 1 and card 2\n";
temp = card2; card2 = card1; card1 = temp;
cout << "Now, where (1, 2, or 3) is the ace of spades? ";
cin >> position;
switch (position)
{
case 1: chosen = card1; break;
case 2: chosen = card2; break;
case 3: chosen = card3; break;
}
if(chosen.number == prize.number &&
//compare cards
chosen.suit == prize.suit)
cout << "That's right! You win!\n";
else
cout << "Sorry. You lose.\n";
getch();
return 0;
}
Ето е едно примерно изпълнение на тази програма:
Card
1 is the seven of clubs
Card
2 is the jack of hearts
Card
3 is the ace of spades
I'm
swapping card 1 and card 3
I'm
swapping card 2 and card 3
I'm
swapping card 1 and card 2
Now,
where (1, 2, or 3) is the ace of spades? 3
Sorry.
You lose.
Втората програма използва масиви, класове и изброени типове. Картите вале, дама, поп и ас са дефинирани като числа, за да може автоматично да се оперира с тях в циклите. Класът card има две променливи, първата, number, показва коя е картата (от двойка до ас), а втората, suit, която е от изброен тип - боята на картата. В главната програма се дефинира масив от 52 карти, наречен deck, което на английски означава тесте (колода) от карти. Разгледайте програмата, а след нея ще може да прочете за някои особености в нея.
//
cardaray.cpp
//
cards as objects
#include
<iostream.h>
#include
<stdlib.h> //
for randomize(), rand
#include
<time.h>
// for randomize()
#include
<conio.h>
// for getche()
enum Suit { clubs, diamonds, hearts, spades };
const
int jack = 11; // from
2 to 10 are
const
int queen = 12; // integers without
names
const
int king = 13;
const
int ace = 14;
class
card
{
private:
int number;
// 2 to 10, jack, queen, king, ace
Suit suit;
// clubs, diamonds, hearts, spades
public:
void init(int n, Suit s) // initialize card
{ suit = s; number = n; }
void display()
// display the card
{
if (number >= 2 && number <= 10)
cout << number;
else
switch(number)
{
case jack: cout << 'J'; break;
case queen: cout << 'Q'; break;
case king: cout << 'K'; break;
case ace: cout << 'A'; break;
}
switch(suit)
{
case clubs: cout << char(5); break;
case diamonds: cout << char(4); break;
case hearts: cout << char(3); break;
case spades: cout << char(6); break;
}
} // end display()
};
// end class card
void
main()
{
card deck[52];
// deck of cards
int j = 0;
// counts thru deck
int num;
// card number
cout << endl;
for (num = 2; num <= 14; num++) // for
each number
{
deck[j].init(num, clubs);
// set club
deck[j + 13].init(num, diamonds); // set diamond
deck[j + 26].init(num, hearts); // set heart
deck[j++ +39].init(num, spades); // set spade
}
cout << "\nOrdered deck:\n";
for (j = 0; j < 52; j++)
// display ordered deck
{
deck[j].display();
cout << " ";
if( !( (j+1) % 13) )
// newline every 13 cards
cout << endl;
}
randomize();
// seed random number generator
for (j = 0; j < 52; j++)
// for each card in the deck,
{
int k = random(52);
// pick another card at random
card temp = deck[j];
// and swap them
deck[j] = deck[k];
deck[k] = temp;
}
cout << "\nShuffled deck:\n";
for (j = 0; j < 52; j++)
// display shuffled deck
{
deck[j].display();
cout << " ";
if ( !( (j + 1) % 13) )
// newline every 13 cards
cout << endl;
}
// stop the flow on the monitor
getch();
} //end main
Най-труден за разбиране е първият цикъл, където картите се инициализират така както са подредени по боя и стойност. Инициализацията става с помощта на функцията init(int n, Suit s) на класа card. Обърнете внимание, че в цикъла се променят две променливи - num и j. По този начин картите се подреждат по бои, от спатия до пика, и във всяка една боя те нарастват от двойка до ас.
Вторият цикъл разпечатва картите на екрана, третия ги размества, а последния разпечатва вече размесените карти на екрана. Непознати функции тук са randomize() и random. Първата инициализира генератора на случайни числа (като обикновено се използва компютърния час) за да може серията от генерирани случайни числа да бъде всеки път различна при всяко едно изпълнение на програмата. Втората функция връща неотрицателно цяло число по-малко от аргумента си, в случая 52 - не забравяйте, че картите са номерирани от 0 до 51, а е от 1 до 52.
Във функцията display() на класа card се използва превръщането на цели числа (които са номерата на съответните ASCII символи) в тип char за да се разпечатат символите на боите спатия, каро, купа и пика. Това превръщане в символа за спатията изглежда така: char(6) .
5. Логически тип (Boolean type). В началото на спецификацията на C++ е нямало логически тип така както, например, в ПАСКАЛ има тип boolean. Този проблем се решава, както се запознахме в част 6, като нулата има значение на лъжа, а всички цели числа, различни от нея - значението на истина. Разбира се, програмистът може да дефинира изброен тип с две стойности по следния начин:
enum boolean (false, true); // false is 0, true is 1
и да работи с променливи от този тип, например:
boolean flag;
if
(alpha < 10)
flag = false;
В следващите спецификации на C++ е определен тип bool, кой е логически тип и който се съпровожда с две предефинирани стойности (predefined literal values) true и false. Следният код показва, че тези стойности могат да се превръщат в цели числа, а именно true е 1, а false - 0. Следният код го демонстрира:
bool
boolvar = true;
cout << "true = " << boolvar << endl;
boolvar
= false;
cout << "false = " << boolvar << endl;
Като дава на екрана следното:
true
= 1
false
= 0
В следващата лекция ще се върнем отново на функциите на класовете и в няколко леции ще се занимаем с тяхното предефиниране (Overloading), аргументи по подразбиране (Default Arguments) и референтни аргументи (Reference Arguments).
Автор: Проф. Процесор Аритметиков
Keywords: С++,
OOP programming , C++ , Classes , Inheritance , Reusability , Creating
New Data Types , Polymorphism and Overloading
Ключови думи: клас
, обект, обектно ориентирано програмиране , полиморфизъм switch if else
?