Понятие статического и динамического связывания
Связывание - подстановка в коды программы вызовов конкретных функций - методов класса. Имеет смысл только для производных классов.
Обычно компилятор имеет необходимую информацию для того, чтобы определить, какая функция имеется в виду. Например, если в программе встречается вызов obj.f(), компилятор однозначно выбирает функцию f() в зависимости от типа адресата obj. Если в программе используются указатели на экземпляры класса: ptr->f(), выбор функции - метода класса определяется типом указателя.
Если выбор функции выполняется на этапе компиляции, мы имеем дело со статическим связыванием.
В этом случае для указателя на базовый класс будет вызвана функция - метод базового класса, даже если указателю на базовый класс присвоить значение адреса экземпляра производного класса.
Если выбор функции выполняется на этапе выполнения программы, мы имеем дело с динамическим связыванием.
В этом случае если при выполнении программы указателю на базовый класс присвоить адрес экземпляра базового класса, будет вызван метод базового класса; если же указателю на базовый класс присвоить адрес экземпляра производного класса, будет вызван метод производного класса.
|
|
По умолчанию для производных классов устанавливается статическое связывание. Если для каких-либо методов класса нужно использовать динамическое связывание, такие методы должны быть объявлены виртуальными.
Виртуальные функции:
- имеют в прототипе в базовом классе ключевое слово virtual;
- обязательно функции-члены класса:
- Во всех производных классах должны иметь такой же прототип (указание слова virtual в производных классах не обязательно).
Если какие-либо методы в производных классах имеют то же имя, что и в базовом классе, но другой список параметров, мы имеем дело с перегруженными функциями.
Пример: классы Точка и Окружность.
class Point{
protected:
int x, y;
public:
...
virtual void print();
};
class Circle: public Point{
private:
int rad;
public:
...
void print(); // можно virtual void print();
};
void Point::print()
{
cout << "Point (" << x << ", " << y << ")";
}
void Circle::print()
{
cout << "Circle with center in "; Point::print();
cout << "and radius " << rad;
}
Использование:
Point p1(3,5), p2(1,1), *pPtr;
Cicle c1(1), c2(p2, 1);
pPtr = &p1; pPtr->print(); // получим: Point (3, 5)
pPtr = &c2; pPtr->print(); // получим:
Circle with center in Point (1, 1) and radius 1
Пример использования динамического связывания: список
Наиболее часто динамическое связывание используется с контейнерными классами, содержащими указатель на базовый класс; в такие контейнерные классы можно включать информацию, относящуюся и к базовому, и к любым производным классам.
|
|
Рассмотрим пример - список, содержащий и точки, и окружности.
struct Item{
Point *info;
Item *next;
// конструктор
Item():info(NULL), next(NULL){}
Item(Point *p):info(p), next(NULL){}
};
class List{
private:
Item *head;
public:
List():head(NULL){}
void insert(Point *p){p->next = head; head = p;}
void print();
};
void List::print()
{
for(Item *cur = head; cur; cur = cur->next){
cur->info->print();
cout << endl;
}
}
Использование класса:
List mylist;
Point *p = new Point(1,2);
mylist.insert(p);
p = new Cicle(1,2,1);
mylist.insert(p);
mylist.print();
получим:
Circle with center in Point (1, 2) and radius 1
Point (1, 2)