Funcții virtuale și clase abstracte

Curs 18. moștenire multiplă

O clasă poate fi generată de orice număr de clase de bază. Având mai mult de o clasă de bază imediată se numește moștenire multiplă.






clasa A;
din clasa B;
clasa C;
clasa D. publice A, B public, privat C;

Un pointer la clasa derivată poate fi trecut la o funcție care se așteaptă ca un pointer la una dintre clasele de bază. Punerea în aplicare a acestui mecanism presupune simpla compilare de tehnici pentru a se asigura că o funcție care se așteaptă un pointer la o clasă de bază, a se vedea partea clasei derivate, diferită de cea care se va vedea funcția pe care se așteaptă ca un pointer la o altă clasă de bază. Funcții virtuale funcționează ca de obicei.
clasa A ; clasa B ; clasa C: Un public, B publică ; void f1 (A * p) f1 ()> void f2 (B p *) f2 ()> void main () g (); // P- Correct> h (); // Eroare: funcția h, nu este un membru al clasei A dynamic_cast (P) -> h (); // Corect>

Clasa nu poate fi specificată ca o clasă de bază imediată mai mult de o dată, dar poate fi mai mult decât o dată clasa de baza indirectă.

clasa A <.>; clasa B: A publică, A publică;

clasa A <.>; clasa B: A publică; clasa C: A publică; clasa D: B publice, C public;

Aici, o facilitate de clasa D va avea două sub-obiect de clasa A.

Clasa derivată și clasele sale de bază pot fi reprezentate ca un grafic aciclic direcționat.

Deoarece obiectul D are două obiect de acționare A. (explicit sau implicit) între A și un pointer la un pointer la un D ambiguu și, prin urmare, interzise.

D * pd = new D; A * pa = pd; pa = (A *) pd; pa = (B *) pd; pa = (C *) pd;

// Ambiguitatea! // Toate aceeași ambiguitate! // Reducerea la un pointer la B Reducerea obiectului // la un pointer la obiectul C.

Cu toate acestea, clasa poate fi atât de clasă directă și indirectă de bază.

clasa A <.>; clasa B: A publică <.>; clasa C: A publică <.>; clasa D: A publică, B publică, C <.>; pa = pd; pa = (B *) pd; pa = (C *) pd;

// Can! // Introducerea unui pointer la obiectul A imediat Reduction D // la un pointer la reducerea obiectului B // la un pointer la obiectul C.

// Funcția A :: f (int) a devenit deschis

// apeleaza functia A :: f (int) // apel Funcția B1 :: f (double) // Apelul funcției B2 :: f (char) // Apelul funcției C :: f (char *)

Prin descriptorul clasă de bază, puteți adăuga cuvântul cheie virtual.
clasa A <.>; clasa B: virtual public A <.>; clasa C: virtual public A <.>; clasa D: B publică, C <.>;

În acest caz, clasa D are doar o singură instanță a clasei A. Grafic, se pare ca acest lucru:

O clasă poate conține clase de baza atât virtuale și non-virtuale de acest tip.
clasa A <.>; clasa B: virtual public A <.>; clasa C: virtual public A <.>; clasa D: A publică <.>; clasa E: B publice, C publică, D publică <.>;

Aici clasa E include două instanțe de clasă A. D. o clasă și o altă clasă de clase virtuale partajate A. B și C.

clasa A <.>; clasa B: virtual public A <.>; clasa C: virtual public A <.>; clasa D: A publică, B publică, C <.>;

// Nu se poate! Aducerea la clasa A este ambiguă.

La determinarea caracteristicilor de clasă cu un programator clasă de bază virtuală, în general, nu se poate ști dacă clasa de bază utilizată în combinație cu alte clase. Aceasta poate fi o problemă cu punerea în aplicare a algoritmilor, care necesită ca funcția de clasă de bază se numește exact o dată. Limba asigură că un constructor de clasă de bază virtuală este numit doar o singură dată. Numita clasă de bază constructor virtuală (direct sau indirect) de constructorul obiectului (constructorul clasei „inferior“ derivate).
clasa A >; clasa B1: virtual public A >; clasa B2: virtual public A >; clasa C: B1 publică, B2 publică >; clasa A >; clasa B1: A publică >; clasa B2: Un public >; clasa C: B1 publică, B2 publică >;

Dacă este necesar, programator poate simula acest circuit, cauzând funcția clasă de bază virtuală a clasei „inferior“ derivate.

O clasă derivată poate suprascrie funcția virtuală a clasei sale directe sau indirecte de bază virtuală. Două clase diferite pot trece peste funcții virtuale de clasă diferită de bază virtuală. În acest fel, mai multe clase derivate poate contribui la punerea în aplicare a interfeței prezentate într-o clasă de bază virtuală.
clasa A

A () <> void g virtual (); void virtuale h ();>; clasa B1: virtual public A ; clasa B2: virtual public A ; clasa C: B1 publică, B2 publică <>; void main ()

// Funcția de apel B1 :: g // Apelul funcției B2 :: h

Dacă două clase înlocuiți aceeași funcție de clasa de bază, care le determină clasă derivată, nu substituie această funcție nu este validă. În acest caz, nu poate fi creat tabelul de funcții virtuale, deoarece acest apel funcție este ambiguă.






clasa A

// Funcție C :: g înlocuirile B1 :: g și B2 :: g // Bine - apelați funcția C :: g // Ambiguitate - B1 :: h sau B2 :: h.

Multe clase oferă o simplă și interfețe comune care urmează să fie utilizate în mai multe moduri diferite. Valoarea exactă a fiecărei funcții de interfață obiect este determinată pentru care este numit. De multe ori între program, emite o cerere și subiectul care le primesc, este un strat de software. În mod ideal, codul intermediar nu trebuie să știe nimic despre tranzacțiile individuale, altfel ar fi trebuit să fie modificat de fiecare dată când modificați un set de operații. În consecință, un astfel de cod intermediar trebuie să treacă pur și simplu cererea destinatarului o parte din datele care reprezintă operațiunea care urmează să fie invocate.

Un mod simplu de a pune în aplicare această abordare este de a trimite reprezentarea șir a operațiunii, care trebuie să fie numit. Cu toate acestea, această linie cineva trebuie să creeze și cineva - decoda pentru a determina ce corespunde operațiunilor. Este destul de dificil și incomod. Ar fi posibil să se trimită numere întregi operații corespunzătoare. Cu toate acestea, deși numărul și convenabil pentru calculator, pentru oamenii din valoarea lor nu este evidentă. Și totuși nevoie de un cod pentru a determina ce doriți să îl apelați.

C ++ oferă un mijloc de referințe indirecte la un membru al clasei. Un pointer la un membru al clasei este o valoare care identifică un membru al clasei. Puteți să-l trateze ca pe un membru al poziției de clasă în clasa obiectului, dar, desigur, compilatorul ia în considerare diferențele dintre funcțiile de date virtuale funcții nonvirtual, etc.

Un pointer la elementul de clasă m este utilizat în combinație cu obiectul. Operatori -> * și. * Se lasă programator să-și exprime secvența următoare. P-> * m m se conectează la obiectul indicat de p. și obj. M * m se leagă la obj obiect. Rezultatul poate fi utilizat în conformitate cu tipul de m. Nu se poate salva prin aceste operațiuni pentru utilizarea sa în viitor.

Desigur, dacă am ști ce un membru al clasei pe care doriți să o utilizați, am putea face acest lucru în mod direct, fără a utiliza indicii. Precum și indicii pentru funcțiile obișnuite, indicatoare către membrii clasei sunt utilizate atunci când este necesar să se facă referire la un obiect al cărui nume nu este cunoscut. Cu toate acestea, spre deosebire de un pointer la o variabilă sau o funcție în mod regulat pointerul membru al clasei este doar un pointer la zona de memorie. Ea corespunde mai mult sau schimbare în structura unui indice în matrice. Combinația dintre un pointer la un membru al clasei cu un obiect sau un pointer la un obiect dă care identifică un anumit membru al unui anumit obiect.
clasa de baza virtual

Baza () <>>; clasa D1: Baza publică void close () void imprimare () >; clasa D2: Baza publică void close () void imprimare () >;

typedef void (Base :: * PF) (); void main () * PF1) (); (Pb -> * PF2) (); (Pb -> * PF3) ();>

// Determina tipul de un pointer la o funcție membru al clasei de baza // stochează un pointer la un membru al clasei // Base (funcții de deschidere și de închidere sunt virtuale, // și funcția de imprimare - non-virtuală). // d - D1 obiect din clasa // functia de apel D1 :: open () // Apelați funcția D1 :: închidere () // Base :: Print (functie de apel) // Pb - un pointer la un obiect de bază de clasă (obiect aparține de fapt clasa D2) // apel D2 funcție :: open () // Apelați funcția D2 :: închidere () // funcția de bază :: Print apel ()

fișier Shapes.h

Forme () static int GetCount () int Stânga () const int Top () const int Dreapta () const int Bottom () const virtuale Egal void () = 0; Muta virtuale void (int unde, forme const * forma) = 0; Forme virtuale * NewShape () = 0; Forme virtuale * Clone () = 0;>;

fișier Shapes.cpp

#include "Shapes.h" int forme :: count = 0;

fișier Storable.h

#define #include stocabil using namespace std; clasa Storable

Storable (); virtuale int Read () = 0; int virtuale Scriere () = 0;>;

fișier Storable.cpp

#include "Storable.h" stocabil :: stocabil (const char * f1, const char * F2) Storable :: Storable (char const * f, modul int) else if (modul == WRITE) > Stocabil ::

Circle.h fișier *

#if. definit (FORME) #include „Shapes.h“ Cercul de clasă # endif: Forme publice

Circle () <> void Draw (); void Move (int unde, Forme const * forma);>;

Fișier Triangle.h *

#if. definit (FORME) #include „Shapes.h“ Triangle clasa # endif: Forme publice

Triangle () <> void Draw (); void Move (int unde, Forme const * forma);>;

fișier Circle_Storable.h

#include "Circle.h" #if. definit (Storable) #include "Storable.h" # endif clasa Circle_Storable: cerc public, virtuale stocabil publice Circle_Storable (const char * f, modul int x = 0, int y = 0, int r = 0, int c = 0): stocabil (f, mode), Circle (x, y, r, c) <>

Circle_Storable () <> int Read (); int Write ();>;

fișier Circle_Storable.cpp

fișier Triangle_Storable.h

#include "Triangle.h" #if. definit (Storable) #include "Storable.h" # endif clasa Triangle_Storable: Triangle publice, virtuale stocabil publice Triangle_Storable (const char * f, modul int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0, int x3 = 0, int y3 = 0, int c = 0): stocabil (f, mode), triunghi (x1, y1, x2, y2, x3, y3, c) <>

Triangle_Storable () <> int Read (); int Write ();>;

fișier Triangle_Storable.cpp

fișier main.cpp

#include "Circle_Storable.h" #include "Triangle_Storable.h" void main () captura (const char * s) pentru (int i = 0; i Draw (); pentru (int i = 1; i Move (Figuri :: STÂNGA, forme [i - 1]); pentru (int i = 0; i Draw (); // Matrice stocate indicii pentru clasa de forme. obiecte reale aparțin clasei sau Circle_Storable Triangle_Storable. // care este o clasă derivată din atât clasa forme. și din clasa Storable. Nu știm la ce anume clasa // (Circle_Storable sau Triangle_Storable) aparține obiectului, dar poate converti un pointer la un pointer la o clasă stocabil. // care, precum și clasa de forme. Acesta este baza pentru ambele clase. Cu toate acestea, acest lucru se poate face numai în timpul programului //, deci utilizați dynamic_cast operatorul tip de conversie. care verifică obiectul aparține unei clase de forme derivate // și clase stocabile. și efectuează conversia. În cazul în care obiectul nu aparține de fapt clasei // derivate din aceste clase, conversia indicele de clasa de forme la un pointer la o clasă stocabil ar fi imposibil, // și operatorul dynamic_cast va returna valoarea 0. pentru (int i = 0; i (Figuri [i]) -> Write (); pentru (int i = 0, n = Forme :: GetCount (); i