Microsoft Visual C и MFC
Наследство
Може би най-важната функция, предоставена на програмиста чрез езика C++, се крие в механизма за наследяване на XE "наследяване". Можете да наследите нови производни класове от предварително дефинирани класове. Класът, от който възниква наследяването, се нарича базов клас. Новият клас се нарича производен.
Производният клас включва елементи от базовия клас и може да ги допълва със свои собствени елементи от данни и методи. Благодарение на наследяването става възможно повторното използване на програмния код.
Произведен клас може сам да служи като базов клас XE "базов клас". Можете да наследите други класове от него. Класът, произтичащ от такова наследяване, ще включва елементи от всички свои базови класове.
Няколко нови производни класа могат да наследяват от един общ базов клас. Самият производен клас може да служи като базов клас за нови класове. По този начин е възможна дървовидна структура на наследяване на класове.
На фигура 1.1 сме дали пример за структурата на наследяване на класа. Трите класа DerivedClassOne, DerivedClassSecond и DerivedClassThird наследяват от един основен клас, BaseClass. Първите два от тях сами действат като базови класове.
Ориз. 1.1. Наследство
Почти цялата структура на библиотеката с класове MFC се основава на механизма на наследяване. Така, например, повечето класове на MFC имат базов клас CObject, който предоставя най-общите свойства на класовете, наследени от него.
Има две основни форми на наследство. Когато производен клас наследява от единичен базов клас - единично наследяване. Наследяване, при което производнотоедин клас се наследява от няколко базови класа едновременно - това е множествено наследяване.
Сингулярно наследяване
В случай на XE единично наследяване "inheritance:single", производният клас наследява само един базов клас. Фигура 1.1 показва наследяване на един клас. Сингулярното наследяване е най-често срещаният метод за наследяване. Библиотеката с класове MFC използва само единично наследяване.
За да се посочи, че даден клас наследява от друг базов клас, името на базовия клас трябва да се появи след името на класа, преди отварящата фигурна скоба на дефиницията на класа. Името на базовия клас трябва да бъде предшествано от двоеточие:
Името на базовия клас може да бъде предшествано от публичен, частен или защитен спецификатор за достъп. Ще разгледаме целта на тези спецификатори в раздела „Разграничаване на достъпа до елементи от базовия клас“. Засега просто ще кажем, че ако не посочите спецификатор за достъп, частният спецификатор ще се приеме по подразбиране.
По-долу сме дефинирали базов клас, съдържащ няколко елемента, и след това сме произвели два нови класа DerivedFirst и DerivedSecond от него. Във всеки от производните класове сме дефинирали различни допълнителни методи и членове на данни.
Класовете DerivedFirst и DerivedSecond сами по себе си могат да действат като базови класове.
Можете да дефинирате елементи в производния клас, чиито имена са същите като имената на елементите на базовите класове. Ако сте направили това заместване, можете да се обърнете към члена на базовия клас, като предоставите пълното му име. Напълно квалифицираното име трябва да се състои от името на класа, към който принадлежи елементът, оператора :: и името на самия елемент.
Като пример, нека вземембазовият клас Base и производният от него клас Derived. И двата класа дефинират елемента от данни iNumber. За достъп до iNumber елемент на базов клас от производен клас, посочете пълното му име Base::iNumber.
На указател към обект от базовия клас може да бъде присвоен указател към обект от класа, получен от него. Тази функция ще се използва широко в библиотеката с класове на MFC.
Множествено наследяване
XE множествено наследяване "inheritance:multiple" работи като единично наследяване. За разлика от единичното наследяване, производният клас може да има множество базови класове. Фигура 1.2 показва пример за наследяване на множество класове. Класът DerivedClaass има два основни класа, BaseClassOne и BaseClassSecond. Класът DerivedClaass и друг клас, BaseClass, се използват при множествено наследяване на класа DerivedClaassSecond.
Ориз. 1.2. Множествено наследяване
Името на единичен базов клас се заменя със списък с имена на базови класове, разделени със запетаи. Спецификаторите за достъп public, private и protect могат да се появят директно преди имената на базовия клас. Ще разгледаме тяхното предназначение в раздела „Разграничаване на достъпа до елементи от базовия клас“.
Редът, в който изброявате базовите класове, засяга само реда, в който се извикват конструкторите и деструкторите на базовия клас. Конструкторите на базов клас се извикват в реда, в който са изброени (отляво надясно). Деструкторите на базов клас се извикват в обратен ред.
Един и същ клас не може да бъде указан два пъти или повече като основен клас (освен ако не е косвен базов клас).
СледващияПримерът дефинира два основни класа, BaseFirst и BaseSecond. Един нов клас Derived е наследен от тях. Полученият клас Derived комбинира елементите на двата базови класа и добавя свои собствени елементи към тях.
Тъй като библиотеката с класове MFC XE "MFC Library" не използва множествено наследяване, няма да навлизаме в нея по-подробно. Ако е необходимо, можете да получите допълнителна информация от справочници или учебници по езика C++ (вижте препратките).
Разграничаване на достъпа до елементи от базовия клас
Вече обсъдихме, че можете да контролирате достъпа до членовете на класа, като посочите спецификатори за достъп за членовете на класа. Членовете на класа, декларирани със спецификатори protected XE „protected“ и private XE „private“, са достъпни само от методите на самия клас. Елементи с публични XE "публични" спецификатори са достъпни не само от методите на класа, но и отвън.
При създаването на производен клас възниква въпросът за достъпа до елементите на базовия клас. Оказва се, че когато наследите клас от базов клас, можете да контролирате контрола на достъпа до членовете на базовия клас. Това, което има значение, е как се декларират членовете на базовия клас и какъв спецификатор за достъп е указан за базовия клас.
От следващата таблица можете да определите как ще се осъществява достъп до членовете на базовия клас в зависимост от спецификатора за достъп на базовия клас и спецификаторите за достъп на член на базовия клас.
Спецификатор за достъп до базов клас
Спецификатор за достъп до елемент на базов клас
Достъпен като публичен
Предлага се като защитен
Предлага се като лично
Предлага се като защитен
Предлага се като защитен
Предлага се като лично
Замяна на методите на базовия клас
В производен клас можете да дефинирате методи и членове на данни с имена, които вече се използват в базовия клас. Съответните методи и членове на данните на базовия клас са скрити. За да се обърнете към тях, трябва да посочите пълното име, което включва името на базовия клас, оператора :: и името на елемента на класа.
XE виртуални методи "виртуални методи"
Методите на основния клас могат да бъдат заменени в производни класове. Ако създадете обект от производен клас и извикате отменен метод върху него, тогава ще бъде извикан методът на производния клас, а не съответният метод на базовия клас. Въпреки това, ако извикате отменен метод на обект на производен клас, като използвате указател или препратка към обект на базов клас, ще бъде извикан методът на базовия клас. С други думи, методът се извиква според класа на указателя към обекта, а не според класа на самия обект.
В C++ можете да посочите, че някои методи на базов клас, които ще бъдат заменени в производни класове, са виртуални. За да направите това, достатъчно е да посочите виртуалната ключова дума преди описанието на метода. Статичният метод не може да бъде виртуален. Методите, декларирани като виртуални в базовия клас, също се считат за виртуални в производните класове.
Ако замените виртуален метод в производен клас и създадете обект от този клас, тогава замененият метод ще се използва независимо от това как е бил извикан. При извикване на заместен метод има значение само класът на обекта, за който се извиква методът.
Всичко, което казахме, не означава, че няма начин да се извика виртуалният метод на базовия клас, ако той е бил заменен. Виртуален метод от базов клас може да бъде извикан чрез указване на пълното му име,включително името на базовия клас.
Виртуалният метод XE "виртуални методи" на базовия клас може да бъде оставен непроменен и да не се отменя в производния клас. В този случай той ще работи като обикновен невиртуален метод.
Следващата програма демонстрира разликата между виртуални и невиртуални методи на клас. Базовият клас Figure дефинира два метода PrintName и PrintDimention, като методът PrintName е дефиниран като виртуален. Класът Rectangle е наследен от класа Figure, в който методите PrintName и PrintDimention са заменени.
Програмата създава обект от класа Rectangle и след това няколко пъти извиква методите PrintName и PrintDimention. В зависимост от това как се извиква методът, методът, дефиниран в класа Фигура или Правоъгълник, ще работи.
Ако стартирате горната програма, тя ще покаже следната информация на екрана:
Абстрактни класове
Виртуалните методи могат да бъдат декларирани като чисто виртуални. За да направите това, след описанието на метода се посочва специален спецификатор (= 0). Това означава, че описаните методи не са дефинирани.
Клас, който дефинира поне един чист виртуален метод, се нарича абстрактни XE "абстрактни класове". Не можете да създавате обекти от абстрактен клас. Абстрактен клас може да се използва само като базов клас за изграждане на други класове.
Клас, извлечен от абстрактен клас, трябва да замени чисто виртуалните методи, описани в него. В противен случай този клас също ще бъде абстрактен.
Като пример за абстрактен клас представяме класа Abstract, който описва чистия виртуален метод PureFunc. Имайте предвид, че този метод не е дефиниран в класа Abstract. Дефиницията на метода се съдържа само впроизводен клас Факт.