2. Обектно-ориентирано програмиране
Цели и принципи
Цели на обектно-ориентирания дизайн
- Резистентност (robustness): възможност за работа с
входни данни, които не са изрично дефинирани при написване на
софтуера
- Адаптивност (adaptability): софтуерът трябва да се
развива с течение на времето в отговор на променящите се условия
- Многократното използване (reusability): кодът
трябва да позволява многократна употреба като компонент на
различни системи в различни приложения
Принципи на обектно-ориентирания дизайн
- Абстрактност (abstraction):
извличане на най-основните части на сложна система и описване на
тези части на прост и точен език
- Капсулиране
(encapsulation): различните компоненти на софтуерната
система не трябва да показват вътрешните детайли на съответните
им имплементации
- Модулност (modularity):
отнася се за организиране на структура, в която различните
компоненти на софтуерната система са разделени на отделни
функционални единици
Наследяване в C++
Наследяването позволява проектирането на общи (базови) класове,
които могат да бъдат специализирани за други (по-частни) класове, с
повторно използване на кода на общия клас от специализираните
(производни) класове.
class Person { // base class
private:
string name; // name
string ssn; // social security number
public:
//...
void print(); // print data
string getName(); // retrieve name
};
class Student : public Person { // derived class
private:
string major; // major subject
int gradYear; // graduate year
public:
//...
void print(); // print data
void changeMajor(string newMajor); // change major
};
Член-функции
Person person(...); // define a person
Student student(...); // define a student
cout << student.getName();// invokes Person::getName()
person.print(); // invokes Person::print();
student.print(); // invokes Student::print();
person.changeMajor("Math"); // ERROR!
student.changeMajor("Math"); // OK
void Person::print() // definition of Person print
{ cout << name << " " << ssn; }
void Student::print() // definition of Student print
{ Person::print(); // first print Person data
cout << major << gradYear;
}
Class Protection
class Base {
private:
int priv;
protected:
int prot;
public:
int publ;
};
class Derived: public Base {
void someMemberFunction()
{ cout << priv; // ERROR: private member
cout << prot; // OK
cout << publ; // OK
}
};
class Unrelated {
Base X;
void anotherMemberFunction()
{ cout << X.priv; // ERROR: private member
cout << X.prot; // ERROR: protected member
cout << X.publ; // OK
}
};
Конструктори и деструктори
Person::Person(const string &nm, const string &ss)
: name(nm), ssn(ss) {} // initializer list
Student::Student(const string &nm, const string &ss,
const string &maj, int year)
: Person(nm, ss), // initialize Person data members
major(maj), // initialize member
gradYear(year) {} // initialize gradYear
Person::~Person() // Person destructor
{ ... }
Student::~Student() // Student destructor
{ ... }
Student* s = new Student(...);
//...
delete s; // calls ~Student() then ~Person()
[person.cpp]
Статично свързване
Person* pp[100];
pp[0] = new Person(...);
pp[1] = new Student(...);
cout << pp[1]->getName(); // OK
pp[0]->print(); // calls Person::print()
pp[1]->print(); // calls Person::print()
pp[1]->changeMajor(...); // ERROR!
Динамично свързване и виртуални функции
class Person { // base class
virtual void print(); // print data
//...
};
class Student : public Person { // derived class
virtual void print(); // print data
//...
};
Person* pp[100];
pp[0] = new Person(...);
pp[1] = new Student(...);
pp[0]->print(); // calls Person::print()
pp[1]->print(); // calls Student::print()
pp[1]->changeMajor(...); // OK
[person.cpp]
Виртуални деструктори
Ако в даден клас се дефинират виртуални функции, тогава трябва да се
дефинира и виртуален деструктор, дори да е празен.
delete [] pp;
When
to use virtual destructors?
In
C++, what’s a virtual destructor and when is it needed?
Полиморфизъм
Способността на променлива да приеме различни типове!
Функции-шаблони
- Минимум на две цели числа:
int min(int a, int b)
{ return (a < b ? a : b); }
- Минимум на две десетични дроби:
double min(double a, double b)
{ return (a < b ? a : b); }
- Обща функция за произволен тип T се нарича функция-шаблон (function
template):
template<typename T> // parameter list
T min(T a, T b) // returns the minimum of a and b
{ return (a < b ? a : b); }
- Функцията-шаблон се извиква за изчисляване на минимума на
обекти от различни типове.
- Може да използва всеки тип, при условие, че операция по-малко
(<) е дефинирана (предефинирана) за този тип.
- Компилаторът разглежда типовете на аргументите на функцията за
да определи коя форма на функцията да приложи - да определи тип
за параметъра на шаблона.
cout << min(3, 4); // invokes min<int>(3,4)
cout << min(6.9, 3.5); // invokes min<double>(6.9, 3.5)
cout << min('t', 'g'); // invokes min<char>('t', 'g')
Класове-шаблони
template <typename Object>
class BasicVector {
Object* a; // array storing an element
int capacity; // length of array a
public:
BasicVector(int cap = 10) // constructor
{ capacity = cap;
a = new Object[capacity];// allocate array storage
}
Object& elemAtRank(int r) // access element at index r
{ return a[r]; }
// ...
};
BasicVector<int> iv(5); // vector of 5 integers
BasicVector<double> dv(20); // vector of 20 doubles
BasicVector<string> sv; // vector of 10 strings
//...
iv.elemAtRank(3) = 8; // iv[3] = 8;
dv.elemAtRank(14) = 2.5; // dv[14] = 2.5
sv.elemAtRank(7) = "hello"; // sv[7] = "hello"
Изключенията са неочаквани събития, които се случват по време на
изпълнението на програмата.
Изключения-обекти
Използване на наследяване за
дефиниране на нови типове за изключения
class MathException { // generic math exception
private:
string errMsg; // error message
public:
MathException(const string& err) { errMsg = err; }
};
class ZeroDivisionException : public MathException {
public:
ZeroDivisionException(const string& err)
: MathException(err) {}
};
class NegativeRootException : public MathException {
public:
NegativeRootException(const string& err)
: MathException(err) {}
};
Изхвърляне (throwing) и прихващане (catching) на изключения
try {
// ...
if (divisor == 0) throw ZeroDivisionException("Division by zero");
//...
}
catch (ZeroDivisionException& zde)
{ // handle division by zero
}
catch (MathException& me)
{ // handle any math exception other than division by zero
}
След изпълнение на catch
блока, изпълнението на програмата продължава с първия оператор след
последния catch блок.
Спецификация на изключенията
void calculator() throw(ZeroDivisionException, NegativeRootException)
{
//...
}
void funct1(); // can throw any exception
void funct2() throw(); // can throw no exception
Общ клас за изключения
class RuntimeException { // generic run-time exception
private:
string errorMsg;
public:
RuntimeException(const string& err) { errorMsg = err; }
string getMessage() const { return errorMsg; }
};
inline std::ostream& operator<<(std::ostream& out, const RuntimeException& e)
{ return out << e.getMessage(); }