Може би помните от т.3 на лекция 33, че в програмата englcon.cpp можехме да запишем следния ред:
E3 = 3 // call again 1-arg constructor
който преобразуваше реалното число 3.0 в обекта E3 от класа English. Ако в същата програма се опитаме да напишем твърднието
float meters = E3; // Error: Cannot convert 'English' to 'float'
компилаторът издава съобщението за грешка:
[C++ Error] : E2034 Cannot convert 'English' to 'float'.
което на практика
означава, че няма оператор за преобразуване на обекти от класа English
в реални стойности. В тази лекция ще научим как става точно такова преобразуване
на обекти от специфициран от нас клас в стойности от предефинираните типове
с помощта на функции-оператори.
.
1. Преобразуване
на предефинираните типове (Type Casting: Conversion for Basic Types).
Досега в няколко от нашите програми компилаторът по подразбиране преобразуваше
между предефинираните типове. Например, даже и в разгледания ред по-горе
от програмата englcon.cpp
E3 = 3 // call again 1-arg constructor
компилаторът има на разположение конструктор, който преобразува реални числа (от типа float), затова той първо преобразува целочисленото число 3 в реалното 3.0 и чак тогава използва едноаргументния конструктор English(float meters).
По същия начин, ако компилаторът срещне израз, в който има аритметични действия между целочислени типове и реално числа от, например типа float, то компилаторът първо преобразува целочислените типове във float и чак тогава извършва действията, в резултат на които получава стойност от типа float.
Друг пример е присвояване на реална стойност на целочислена. Например следният код
int a;
float b = 2.718182;
a = b;
ще даде стойност на a, равна на 2.
Разбира се, така написан третият ред е пример за лоша програмистка практика, особено в големи програми, защото е трудно да се разбере от пръв поглед, че дробната част на b се губи. Много по-добър запис е
int a;
float b = 2.718282;
a = int(b);
който дава същия резултат, но един програмист веднага ще разбере, че става дума за "взимане" само на целочислената стойност на b. Изразът int(b) се нарича на английски type casting, което може да се преведе като преобразуване на типовете.
Този запис е по-скоро в стила на C (C style), и затова в C++ има друг запис, който се нарича на английски static type casting или static_cast approach, което може да се преведе като статично преобразуване на типовете. Той има следния вид:
a = static_cast<int>(b);
Предполага се, че създателите на C++ са имали предвид по-явния вид на горния запис и неговата уникалност, която помага по-бързо да бъде потърсен с интегрираната работна среда.
! Има и друг запис на преобразуването на типове, но той не се препоръчва в ръководствата по C++!
a = (int)b; // alternative syntax (not recommended)
Въпреки автоматичното преобразуване, има случаи когато е необходимо изрично (explicitly) да се преобразува даден тип в друг, за да се изпълни замисленият алгоритъм. Ето един пример от курса на Лафоор [1]:
float
parts(const float & orig, float & fracpart)
{
float wholepart = int(orig); // find whole part
fracpart = orig - wholepart; // find fractional part
return wholepart;
// return whole part
}
Целта на тази функция е да върне цялата част и дробната част на едно реално число - случая то се дава с аргумента orig. И двете части се връщат в променливи от тип float - целочислената стойност се връща от функцията с твърдението return wholepart;, а дробната с променливата fracpart, която е аргумент по връзка, т.е. може да се променя. Например при orig = 2.718282, функцията ще върне 2.0, а аргументът fracpart ще е 0.718282.
2. Превръщане на обекти от потребителски класове в такива от предефинираните типове. Много често с обектите на потребителските дефинирани класове са свързани стойности от типа на предефинираните типове, като int, float или double. Например, всяко едно комплексно число има модул (наречен още абсолютна стойност), който е равен на корен квадратен от сумата от квадратите на реалната и имагинерната част. Този модул е реално число и донякъде е уместно, ако присвояваме едно комплексно число на реално, то да се присвоява неговия модул, т.е. реалното число да става равно на модула на комплексното.
Друг хубав пример е да се изчисляват минутите в обект от класа airtime от лекция 12 или пък да се превръща автоматично едно разстояние (което е във футове и инчове) от класа English от лекция 33 в метри.
Функцията оператор за модула на комплексното число ще е следната:
operator float()
{
return sqrt(real*real + imaginary*imaginary);
}
Може да я прибавите в програмата complex4.cpp на лекция 39, а също и да прибавите следните редове в главната функция за да тествате преобразуването.
// find module and ...
float module = c1;
// ... display the result
cout << "|";
c1.display();
cout << "| = " << module;
cout << endl << endl;
Ключовият ред по-горе е
float module = c1;
Така написан, той е инициализиране на променливата module от типа float с обекта c1 от класа complex_number. Но можете да напишете и два реда
float module;
module = c1;
като вторият от тях е присвояване на стойността на обекта c1 на реалната променлива module. Разбира се, най-ясният запис би бил
float module;
module = float(c1);
или
float module;
module = static_cast<float>(c1);
! Разбира се, донякъде е спорно, доколко е уместно да присвояваме комплексни числа на реални. При работа в колектив при голяма програма, един друг програмист би бил затруднен да разбере за какво точно става въпрос - дали твърдението module = c1; връща модула на комплексното число, неговата реална или имагинерна част (последните две също представляват реални числа).
3. Превръщане на обекти от класа English в реални (Conversion from English to float). Ще се спрем на един пример от курса на Лафоор [1] за превръщане на обекти от класа English, който поддържа разстояние във футове и инчове, в разстояние в метри. В програмата englconv.cpp, дадена по-долу, функцията-оператор за това превръщане е следната:
// convert English to float
operator float()
{
float fracfeet = inches/12; // inches to feet
fracfeet += float(feet); // add the feet
return fracfeet/MTF;
// convert to meters and return
}
Операторът има име, еднакво с типа, който се получава при преобразуването - в случая float. Пред ключовата дума operator няма тип на върнатата стойност - тя се подразбира от името на оператора. В тялото на функцията-оператор първо се изчисляват колко фута са инчовете, като се делят последните на 12, защото в един фут има 12 инча. След това към тази стойност се прибавят футове, като преди това се превръщат в реална променлива от тип float с кастинга float(feet).
! Присвояването
fracfeet += float(feet);
е еквивалентно на
fracfeet = fracfeet + float(feet);
Ето и цялата програма, в която сме добавили няколко реда повече в главната функция, за да си изясни читателят как точно работи преобразуването между двата типа.
//
englconv.cpp
//
converts English objects to meters
#include
<iostream.h>
#include
<conio.h>
// for getch()
class
English
// English distances
{
private:
int feet;
float inches;
static const float MTF;
// meters to feet, declaration
// no-argument constructor
public:
English() : feet(0), inches(0.0)
{ }
// 1-argument constructor
English(float meters)
{
float fltfeet = MTF*meters; // get decimal feet
feet = int(fltfeet);
// integer part is feet
inches = 12*(fltfeet - feet); // remainder is inches
}
// 2-argument constructor
English(int f, float i) : feet(f), inches(i)
{ }
// user input
void get()
{
cout << " Enter feet: "; cin >> feet;
cout << " Enter inches: "; cin >> inches;
}
// display
void display()
{
cout << feet << "\'-" << inches << '\"';
}
// convert English to float
operator float()
{
float fracfeet = inches/12; // inches to feet
fracfeet += float(feet); // add the feet
return fracfeet/MTF;
// convert to meters and return
}
}; // end English class
const float English::MTF = 3.280833; // meters to feet, definition
void
main()
{
English engman(1.70);
// meters to English
cout << "\nengman = ";
engman.display();
// display English
float metman;
//
works also!!! but it is not prefered approach
//
metman = (float)engman;
// English to meters
metman = float(engman);
// English to meters
cout << "\nmetman = " << metman; // display
meters
// intialization with conversion
float sasha = English(5, 8.0);
cout << "\nSasha = " << sasha; // display meters
// intialization with conversion
float ivan(English(5, 5.0));
cout << "\nIvan = " << ivan; // display meters
// implicit cast !!! not prefered !!!
metman = engman;
// English to meters
cout << "\nmetman = " << metman; // display
meters
// explicit cast !!! prefered !!!
metman = static_cast<float>(engman);
// English to meters
cout << "\nmetman = " << metman; // display
meters
getch();
}
// main()
Обърнете внимание, че за целта на превръщането, в класа сме декларирали една статична константа
static const float MTF; // meters to feet, declaration
която се инициализира (дефинира) извън класа
const float English::MTF = 3.280833; // meters to feet, definition
със стойността 3.280833, която показва колко фута има в един метър.
4. Превръщане на обекти от класа xString в низове (Conversion from xString to String). Друг пример от курса на Лафоор [1] е за превръщане на обекти от класа xString, който поддържа стингови (низови) променливи, в низове (стрингове) от типа String (char*). Класът xString беше въведен в т.6. на лекция 19. Смисълът на това превръщане е да могат да се използват многото функции, които работят с низове. В следващата програма, такава е функцията stricmp(), използвана за сравняване на стрингове. Ето цялата програма:
//
strconv.cpp
//
converts xString to normal C string
#include
<iostream.h>
#include
<string.h>
// for strlen(), strcpy(), etc.
#include
<conio.h>
// for getch()
class
xString
{
private:
enum { MAX = 80 };
// maximum length of xStrings
char str[MAX];
// ordinary C string
public:
xString()
// no-arg constructor
{ strcpy(str, ""); }
xString( char s[] )
// convert string to xString
{ strcpy(str, s); }
void input()
// get string from user
{ cin.get(str, MAX); }
void display()
// display string
{ cout << str; }
operator char*()
// convert xString to string
{
return str;
}
};
void
main()
{
xString s1("George");
// constructor converts
// string to xString
xString s2;
cout << "Enter your name: ";
s2.input();
// get s2 from user
// convert s1, s2 to strings
if (stricmp(s1, s2) == 0)
// compare them with stricmp()
cout << "You're George!";
else
cout << "You aren't George.";
getch();
}
// main()
Обърнете внимание, че функцията-оператор се казва char*(), където звездата след символния тип char (char*) означава указател към типа char. В следващите лекции ще се запознаем с указателите, наречени на английски pointers, но за сега примете, че като цяло двете дефиниции на променливи от тези типове
char * char_arr_var1 = "Some string";
и
char char_arr_var2[] = "Some string";
са еквивалентни., което означава че редът
cout << stricmp(char_arr_var2, char_arr_var1) << endl;
би дал, че са еднакви (0 на екрана).
В програмата превръщането на обекти от класа xString в низове става по-подразбиране (implicitly), чрез извикването на функцията stricmp() с аргументи от класа xString.
stricmp(s1, s2)
Ако двата обекта s1 и s2 от класа xString имат еднакви стрингове (пазени в променливата str), то тази функция връща нула, ако имат различни - връща число, различно от нула.
! Спомнете си, че функцията stricmp() сравнява низове без да обръща внимание, дали буквите са големи или малки (case insensitive)!
В следващата
лекция ще се занимаем с превръщането на обекти от различни класове
един в друг (Conversions Between Classes).
.