Лекции по обектно-ориентирано програмиране



страница14/16
Дата25.07.2016
Размер0.95 Mb.
#6568
ТипЛекции
1   ...   8   9   10   11   12   13   14   15   16

26. Пример за клас дата.

ще дефинираме клас Date, който използва предефинирана операция ++ за увеличаване с 1 в префиксна и постфиксна форма;


#include

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; в общия случай, достъпът до несъществуващи данни елементи не е опасен, но извикването на несъществуваща функция елемент може да доведе до необратими грешки в програмата;
възможно е с помощта на предефинирана операция за присвояване или конструктор за преобразуване на обект от производен клас да се присвои обект от съответен базов клас; такова присвояване в общия случай би оставило неопределени собствените елементи на производния клас;




Сподели с приятели:
1   ...   8   9   10   11   12   13   14   15   16




©obuch.info 2024
отнасят до администрацията

    Начална страница