class Date
{ friend ostream &operator<< (ostream &, const Date &);
public:
Date (int = 1, int = 1, int = 1900);
void setDate (int, int, int);
Date operator++ ();
Date operator++ (int);
const Date &operator+= (int);
int leapYear ();
int endofMonth ();
private:
int day; int month; int year;
static int days[]; void helpIncrement ();
} ;
int Date::days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} ;
Date::Date (int d, int m, int y)
{ setDate (d, m, y); }
void Date::setDate (int d, int m, int y)
{ month = (m>=1 && m <=12) ? m : 1;
year = (y >= 1900 && y <=2100) ? y : 1900;
if (month == 2 && leapYear())
day = (d >= 1 && d <= 29) ? d : 1;
else day = (d >= 1 && d <= days[month]) ? d : 1;
}
Date Date::operator++ ()
{ helpIncrement ();
return *this; //връща се копие на текущия обект с променена дата
}
Date Date::operator++ (int f)
{ Date temp = *this;
helpIncrement ();
return temp;
}
const Date &Date::operator+= (int addDays)
{ for (int i = 1; i <= addDays; i++)
helpIncrement ();
return *this; //позволява слепване
}
int Date::leapYear ()
{ if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
return 1;
return 0;
}
int Date::endofMonth ()
{ if (month == 2 && leapYear ())
return day == 29;
return day == days[month];
}
void Date::helpIncrement ()
{ if (endofMonth () && month == 12)
{ day = month = 1; year++; }
else if (endofMonth())
{ day = 1; month++; }
else day++;
}
ostream &operator<< (ostream &output, const Date &d)
{ static char *monthName[13] = { “”, “Януари”, “Февруари”, “Март”,
“Април”, “Май”, “Юни”, “Юли”, “Август”, “Септември”, “Октомври”,
“Ноември”, “Декември” } ;
output << d.day << ‘ ‘ << monthName[d.month] << “, “ << d.year;
return output;
}
void main ()
{ Date d1, d2 (27, 12, 1992), d3 (0, 99, 8045);
cout << d1 << endl << d2 << endl << d3 << endl;
cout << (d2+=7) << endl;
d3.setDate (28, 2, 1992);
cout << ++d3 << endl;
Date d4 (18, 3, 1969);
cout << ++d4 << endl << d4 << endl;
cout << d4++ << endl << d4 << endl;
}
за да се предефинира операцията ++ с възможност да се използва в префиксна или постфиксна форма, трябва двете предефинирани операции да имат различни сигнатури за да бъдат различавани от компилатора;
префиксният вариант се дефинира както всяка унарна операция; например в горната програма, ако d1 е обект от клас Date и компилаторът срещне израза ++d1, той ще генерира на негово място израза d1.operator++ (); префиксната форма на операцията ++ може да се реализира и с приятелска функция на класа, която се описва в дефиницията на класа така:
friend Date operator++ (Date &);
тогава компилаторът заменя израза ++d1 с operator++ (d1);
при постфиксния вариант уникалната сигнатура на предефинираща функция се постига с помощта на фиктивен аргумент от тип int;
например в горната програма, ако d1 е обект от клас Date и компилаторът срещне израза d1++, той ще генерира на негово място израза d1.operator++ (0); постфиксната форма на операцията ++ може да се реализира и с приятелска функция на класа, която се описва в дефиницията на класа така:
friend Date operator++ (Date &, int);
тогава компилаторът заменя израза d1++ с operator++ (d1, 0);
27. Наследяване на класове. Базови и производни класове.
Наследяването е начин за повторно използване на програмно осигуряване; програмистът може да укаже, че създаден нов клас наследява данните елементи и функциите елементи на по-рано дефиниран клас; наследяващият клас се нарича производен клас, а наследеният – базов клас; всеки производен клас може да се използва като базов при създаването на нови производни класове;
при просто наследяване, производният клас наследява само един базов клас; при множествено наследяване, производният клас наследява два или повече базови класа;
в общия случай, производният клас добавя свои собствени данни и функции елементи, така че обикновено той е по-голям от своя базов клас; в този смисъл, производният клас е по-специфичен по своето предназначение, в сравнение със своя базов клас;
основното предимство на наследяването е във възможността в производния клас да се определят добавки, замени или усъвършенствания на чертите, унаследени от базовия клас;
при наследяване могат да се използват всички съществуващи библиотеки от класове; в този смисъл, значителна част от програмното осигуряване може да се конструира с използването на стандартизирани компоненти;
производният клас няма достъп до закритите елементи на своя базов клас; разрешаването на такъв достъп би нарушило инкапсулацията на базовия клас; производният клас, обаче, има достъп до откритите и защитените елементи на своя базов клас;
производният клас би могъл да има достъп до закритите елементи на своя базов клас, например чрез функции за достъп, предвидени в открития интерфейс на базовия клас;
както знаем, откритите елементи на базовия клас са достъпни за всички функции в програмата, закритите елементи на базовия клас са достъпни само за функциите елементи и за приятелските функции на този клас; защитеното ниво на достъп (protected) на базовия клас е междинно ниво между открития и закрития достъп; защитените елементи на базовия клас са достъпни за елементите и приятелите на базовия клас и за елементите и приятелите на производния клас; в производния клас, този достъп се осъществява само с имената на тези елементи;
базовият клас може да се наследява от производния като public (открито), protected (защитено) или private (закрито);
-
при наследяване от тип public, откритите елементи на базовия клас стават открити елементи на производния клас и защитените елементи на базовия клас стават защитени елементи на производния клас;
-
при наследяване от тип protected, откритите и защитените елементи на базовия клас стават защитени елементи на производния клас;
-
при наследяване от тип private, откритите и защитените елементи на базовия клас стават закрити елементи на производния клас;
и при трите вида наследявания, производният клас няма достъп до закритите елементи на базовия клас;
базовият клас може да бъде пряк или косвен базов клас на производния клас; прекият базов клас явно се указва в дефиницията на производния клас, за разлика от косвения базов клас, който се наследява през две или повече нива в йерархията на класовете;
при открито наследяване (от тип public), обектите на производния клас могат да се разглеждат и като обекти на базовия клас; това не е вярно при закрито или защитено наследяване;
обектите на базовия клас не могат автоматично да се разглеждат като обекти на производния клас; програмистът може явно да преобразува типа на указател към базовия клас в тип на указател към производния клас; типична грешка е този указател да се използва за достъп до несъществуващи елементи на производния клас;
Примерна програма:
#include
#include
class Point
{ friend ostream &operator<< (ostream &, const Point &);
public:
Point (float = 0.0, float = 0.0);
void setPoint (float, float);
float getX () const { return x; }
float getY () const { return y; }
protected:
float x, y;
} ;
Point::Point (float a, float b)
{ setPoint (a, b); }
void Point::setPoint (float a, float b)
{ x = a; y = b; }
ostream &operator<< (ostream &output, const Point &p)
{ output << ‘[‘ << p.x << “, “ << p.y << ‘]’;
return output;
}
class Circle:public Point
//класът Circle открито наследява класа Point; тук ‘:’ е указание за //наследяване, ключовата дума public указва типа на наследяването
{
friend ostream &operator<< (ostream &, const Circle &);
public:
Circle (float = 0.0, float = 0.0, float = 0.0);
void setRadius (float);
float getRadius () const;
float area () const;
protected:
float radius;
} ;
Circle::Circle (float r, float a, float b) : Point (a, b)
{ radius = r; }
void Circle::setRadius (float r)
{ radius = r; }
float Circle::getRadius () const
{ return radius; }
float Circle::area () const
{ return 3.14159*radius*radius; }
ostream &operator<< (ostream &output, const Circle &c)
{ output << “Центърът = [“ << c.x << “, “ << c.y << “]; Радиусът = “
<< setiosflags (ios::showpoint) << setprecision (2) << c.radius;
return output;
}
void main ()
{ Point *pointPtr, p (3.5, 5.3);
Circle *circlePtr, c (2.7, 1.2, 8.9);
cout << p << endl << c << endl;
pointPtr = &c;
cout << *pointPtr << endl;
circlePtr = (Circle *) pointPtr;
cout << *circlePtr << endl;
cout << “Лицето = “ << circlePtr->area () << endl;
pointPtr = &p;
circlePtr = (Circle *) pointPtr;
cout << *circlePtr << endl << “Лицето = “ << circlePtr->area () << endl;
}
ще отбележим, че конструкторът на Circle извиква конструктора на Point за да зададе начални стойности на частта от обектите на класа Circle, която се отнася до базовия клас Point; това става чрез списък от инициализатори на елементи; ако конструкторът на Circle не активира конструктора на Point, то конструкторът на Point се активира със стойности по премълчаване; ако класът Point няма конструктор с елементи по премълчаване, данните елементите на класа Point ще имат неопределени стойности;
предефиниращата функция на операцията за извеждане в поток на класа Circle има непосредствен достъп до защитените данни елементи x и y на класа Point, тъй като тази функция е приятелска на производния клас Circle;
указател към производния клас се присвоява винаги правилно на указател към базовия клас, тъй като обектите на производния клас могат да се разглеждат като обекти на базовия клас; компилаторът извършва такова преобразуване неявно; указателят към базовия клас вижда само тази част от производния клас, която се отнася към базовия клас; такова присвояване може да стане и чрез псевдоним, например: Point &pRef = c – дефиниране на псевдоним на обект от базовия клас Point, който става псевдоним на обект от производния клас Circle;
присвояването на указател към базовия клас на указател към производния клас е опасно и в този случай компилаторът не извършва неявно преобразуване – затова трябва да се използва явно такова; разглеждането на обект от клас Point като обект от клас Circle води до получаване на неопределена стойност за данната елемент radius; в общия случай, достъпът до несъществуващи данни елементи не е опасен, но извикването на несъществуваща функция елемент може да доведе до необратими грешки в програмата;
възможно е с помощта на предефинирана операция за присвояване или конструктор за преобразуване на обект от производен клас да се присвои обект от съответен базов клас; такова присвояване в общия случай би оставило неопределени собствените елементи на производния клас;
Сподели с приятели: