- Тәсілді шақыру ерекшеліктері
Жоғарыда қарастырылған мысалда (жанып-өшетін объект) Blink тәсілін (белгілі-бір уақыт аралығында жану) қолдану тілегі туындауы мүмкін. Әрине бұл тәсіл белгілі-бір уақыт аралығы өткен сайын барлық үш класс үшін де Show мен Hide тәсілдерін шақырып отыру керек. Бірақ әрқайсы класс үшін өзінің Show мен Hide-ы шақырылады.
Tblinker класына осы тәсілді ендірейік
procedure TBlinker.Blink;
begin
while not keypressed do begin
self.Show;
delay(100);
self.Hide;
delay(100);
end;
end;
Айталық, бұл тәсіл барлық класстарға мұраға берілсін. Көрсеткіш жарияланып, ұрпақ-класс экземпляры құрылған болсын:
var ColorBlinker:PColorBlinker;
new(ColorBlinker);
ColorBlinker^.Init(10,10,10);
ColorBlinker^.Blink;
Не жанып-өшеді? Ақ нүкте? Неліктен?
Компилятор қайсы тәсілді шақыруды осы тәсіл қайсы объектке қатысты шақырылып тұрған болса сол объект экземплярының типі туралы мәліметке сүйеніп шешеді. ColorBlinker^ -дің типі – TcolorBlinker, демек Blink тәсілі осы класстан шақырылып отыр. Бұл тәсілді мұраға алған, сондықтан өзініңкі жоқ. Барлық үш класс үшін де тәсіл — сол біреу ғана.
Өзіңізді компилятордың орнына қойыңыз да Blink тәсілін компиляция жасап көріңіз. Blink тәсілінің ішінде Show және Hide шақырулары тұр. Олар Self-ке қатысты шақырылып тұр. Self-ңтипі қандай? Tblinker, өйткені тәсіл осы класста ендірілген. Демек Blink тәсілінің кодында дәл TBlinker.Show және TBlinker.Hide тәсілдерінің шақырулары тұр. Сондықтан да нүкте ақ түсті.
- Паскалдағы кеш байланысу
Бұл жағдайдан былай шығуға болады: Blink тәсілін сол бір ғана бастапқы кодымен үш класстың әрбіріне көшіріп шығу керек. Бұл ыңғайлы болмайды (және кейбір мүмкіндіктер жоғалуы мүмкін).
Басқа бір жолы – бұл тәсілді кеш байланысу механизмімен қолдану. Сонда Show және Hide тәсілдері қайда орналасқандығын оның көрсеткіші қалай сипаттталғандығына қарап анықтауға емес(көрсеткіш өзінің классынан басқа кез-келген ұрпақ-класстың экземплярын көрсетіп тұруы мүмкін, ал ұрпақ класстың тәсілдері өзініңкі болуы мүмкін), тікелей объекттің өзінің эземплярынан алуға болады.
Тәсілкеш байланысу механизмін қолданып шақырылуы үшін, ол тәсіл виртуал не динамикалық деп жариялануы керек.
Компилятор программада виртуал не диамикалық тәсілді кездестірген уақыттта мағынасы мынадай болатын ерекше кодты генерациялайды:
1) Тәсіл қайсы объекттің экземплярына қатысты шақырылып тұрса, сол объекттің экземплярынан виртуалді тәсілдер адресі жазылған талица көрсеткішін алу. Бұл көрсеткіш осы экземпляр тиісті болған класс үшін бар болады.
2) Таблицадан керекті тәсілдің адресін іздеп табу(динамикалық және виртуал тәсілдер үшін іздеу жолы әртүрлі)
3) Тәсілдің айқын параметрлерін және шақырылатын экземплярдың көрсеткіші Self-ті алдын ала стекке орналастырып алып, көрсетілген адрес бойынша шақыру жасау.
Осылайша, компилятор шақырылатын тәсілдің адресін объектті білдіретін айнымалы(не оның көрсеткіші) типіне қарап емес, объект денесіндегі мәліметтер негізінде анықтайды.
Жалпы барлық тәсілдер осылай шақырылуы керек. Алайда, виртуал, әсіресе динамикалық тәсілді шақыру қарапайым статикалық тәсілді шақыруға қарағанда ұзақ уақыт талап етеді. Бұл таблицадан тәсіл адресін іздеу үшін қосымша әрекеттер орындалатындығынан. Сондықтан, программа тиімділігін жоғарылату үшін кеш байланысу механизмін талап етпейтін тәсілдерді статикалық деп жариялап қойған дұрыс.
Әдетте, виртуалды(динамикалық) сияқты, мұрагерлік тізбегі үшін ортақ боп табылатын, бірақ ұрпақ-класстарда әр-түрлі реализацияны талап ететін хабарламаларға реакция (жауап) жасайтын тәсілдерді жариялап қойған дұрыс.
Басқаша айтқанда, егер осы тәсілді, аталық-класстың әртүрлілігі деп ұрпақ-класстың экземплярында шақырмақшы болсақ, бұл сұранысты тани алуы керек болады.
(Другими словами – если вы собираетесь вызывать этот метод у экземпляра класса потомка, считая, что он – разновидность предка, поэтому должен как-то понимать этот запрос.)
Егер берілген класстың ұрпақтары жоқ болатын болса, онда оның тәсілдерін виртуалды ретінде жариялаудың маңызы жоқ.
Әрекетті орындайтын классты дәл анық білген жағдайда немесе ұрпақ-класста қайта анықталмайтын жағдайда да тәсілдерді виртуал етудің қажеті жоқ. Дербес жағдайда, бұл – класс экземпляры құрылғаннан кейін артынша шақыруға арналған инициализация тәсілдері боп табылады. Бұл кезде оның әрекетінің мәнін «аталық-класстың мына әртүрлілігін инициализация жасау» деп емес, «мына объектті мұрагерлік тізбегіне қатысы жоқ, дәл өзінің классының экземпляры ретінде инициализация жасау» деп ұғыну керек.
Жоғарыда қарастырған өшіп-жанатын объект және Blink әрекеті мысалында Show және Hide тәсілдері үшін мұрагерліктің барлық тізбегінде кеш байланысу механизмін қолданудың мәні бар болар еді. Blink тәсілінің өзін аталық-класста ғана жариялап қоюға болар еді, дәл осы Blink тәсілін кеш байланысумен шақыру талап етілмейді.
Как бы он работал:
procedure TBlinker.Blink;
begin
while not keypressed do begin
self.Show; delay(100);
self.Hide; delay(100);
end;
end;
Self.Show және Self.Hide-ты шақыру кезінде ол Self көрсеткішін алып, ондағы адрес бойынша виртуал тәсілдердің адресі жазылған таблицаны тауып, сол арқылы Show немесе Hide-ты шақырар еді. Маңыздысы мынада, егер осы Blink тәсілі TBlinker классы ұрпағының экземплярына қатысты шақырылса, онда Self ұрпақ- экземплярды көрсетіп тұрар еді, демек таблица да ұрпақ- экземплярыдыңкі болар еді, және TBlinker классының тәсілдері емес, керекті класстың тәсілдері шақырылар еді.