Похожие презентации:
Л10.Ч2. ГрафПример. Раннее и позднее связывание
1. Пример множественного наследования:
Порождение класса Kolobok из двухбазовых классов Bar и Circ
Bar ::
x, y, h, w
Circ::
x, y, r
r
.x,y
X, Y
Kolobok::
name, numb
h
.
Пётр
name
1
numb
w
2.
(x,. y)h
w
class Bar
{ int h, w, x, y;
public:
Bar(int hh, int ww, int xx, int yy)
{if (hh<0 || ww<0 || xx<0 || yx<0) throw “error”;
h = hh; w = ww; x = xx; y = yy; }
void Show()
{ cout<<“Bar: LUPoint(”<<x<<“, ”<<y<<“) ”;
cout<<“width=”<<w<<“height=”<<h<<endl;}
};
3.
(x, y) r.
class Circ
{int r, x, y;
public:
Circ(int rr, int xx, int yy)
{if (hh<0 || ww<0 || xx<0 || yx<0) throw “error”;
r = rr; x = xx; y = yy;}
void Show()
{cout<<“Circle: CentrePoint(”<<x<<“, ”<<y<<“) ”;
cout<<“radius =”<<r<<endl;}
};
4. class Kolobok
class Kolobok: public Circ, public Bar{ int numb; char *name; // имя и место
public:
Kolobok(int h, int w, int x, int y, int r, int m, char *s):
Bar(h, w, x, y), Circ(r, x + w/2, y-r)
{ numb = m; name = new char [ strlen(s) + 1];
strcpy(name, s);}
Инициализация
~Kolobok() { delete [] name;} собственных ч/д
Kolobok(Kolobok&);
Kolobok operator = (Kolobok &);
void Show(); };
5.
void Kolobok :: Show(){ Bar :: Show();
Circ :: Show();
cout<<“name=”<<name<<
cout<<“number=”<<numb<<endl;
6. Пример использования
void main(){ try{ int x = 150, dx = 100, i;
Kolobok klb[3] =
{ Kolobok(100, 50, x,150, 40, 2, “Пётр”),
Kolobok(150, 50, x=x+dx,100, 35, 1, “Лена"),
Kolobok(70, 50, x+dx, 180, 30, 3, “Иван")};
for (i = 0; i < 3; i++) klb[i].Show();
7. 5. Полиморфизм, раннее и позднее связывание, виртуальные функции
Полиморфизм от греческих слов poly (много),множественность форм.
morphos (форм) -
Практически с понятием полиморфизма мы уже
сталкивались - это перегрузка функций и операций.
При наследовании оно тоже используется
понятие виртуальных функций.
через
8. 5.1. Раннее (статическое) и позднее (динамическое) связывание
Вспомним пример классов A и B из п.1.class A { private: int d1, d2, d3;
public: A() {d1 = d2 = d3 = 0;}
A(int x, int y, int z) {d1 = x; d2 = y; d3 = z;}
void PrintA();
};
class B: public A
{ int d3, d4;
public: B();
B(int, int, int);
void PrintB();
… };
9.
Рассмотрим вариант, когда функции выводачлен-данных в этих классах имеют одинаковое
имя Print()
void A::Print(){ cout<<d1<<’ ‘<<d2<<’ ‘<<d3<<’ ‘;}
void B::Print(){ A::Print();cout<<d3<<‘ ‘<<d4;}
Функции Print() нарушают условия перегрузки
для обычных функций: у них не только
одинаковое имя, но и список аргументов.
10. Однако это не приведет к сообщению об ошибке компилятором, так как, например, в таком фрагменте A x; B y; x.Print(); // A ::
Print()y.Print(); // B :: Print()
выбор функции компилятор
объектов: x или y.
определяет
по
типу
Причем этот выбор осуществляется еще на этапе
компиляции.
11.
Такое связывание вызова функции с конкретнойфункцией (определение её адреса в ОП) на этапе
компиляции называется ранним или статическим
связыванием.
Но, используя свойства преобразования объектов,
ссылок и указателей порожденных классов к
базовому, программы можно делать более гибкими.
Что имеется в виду?
12.
В некотором месте программы (в одном и том же )может вызываться функция Print() из любого класса,
причем компилятор на этапе трансляции не может
определить адрес вызываемой функции, но он может
задать алгоритм вычисления этого адреса при
выполнении программы.
НАПРИМЕР, пусть определены классы А, В, С:
class A
{private: int d1, d2, d3;
public:
A() {d1=d2=d3=0;}
A(int x; int y, int z) {d1=x; d2=y; d3=z;}
void Print() {cout<<d1<<‘ ‘<<d2<<‘ ‘<<d3;}
};
13. class B: public A { int d3, d4; public: B() { d3 = d4 = 0;} B(int a, int b, int c, int d, int r) : A(a, b, c), d3(d), d4(r) { }
void Print(){ A :: Print(); cout<<d3<<’ ‘<<d4;}..........................
};
14. class C: public A { int d5; public: C(){ d5 = 100;} C(int a, int b, int c, int d):A(a, b, c), d5(d) {} void Print(){ A ::
Print(); cout<<’ ‘<<d5;}...........................
};
15. и программа:
void main(){A x; B y(1, 2, 3, 4, 5, 6); C z; A *pa;
int k;
cin>>k;
switch(k )
Преобразование указателей
{ case 0: pa = &x; break;
порожденного класса к базовому
case 1: pa = &y; break;
(правило 2)
case 2: pa = &z; break;
default: cout<<“СТОП!”; return;
}
pa->Print();
...
}
16.
В этом примере до исполнения программынеизвестно, адрес какого объекта будет
занесен в указатель pa и, следовательно,
функция Print() из какого класса - A, B или C
будет вызвана.
Другими словами, связать вызов функции Print()
с конкретным экземпляром этой функции
возможно только при выполнении программы
по специальному алгоритму.
Это и есть так называемое позднее или
динамическое связывание.
17. Увы
УвыНо в приведенном выше примере будет пока
вызываться функция A::Print() и выводить
базовую часть любого объекта.
Чтобы происходил действительно вызов разных
функций в зависимости от значения указателя
pa,
надо
функцию
Print()
объявить
виртуальной.
Программирование