Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W "pojedynczym dziedziczeniu", które jest typową formą dziedziczenia, klasy mają tylko jedną klasę bazową. Rozważmy relację pokazaną na poniższym rysunku.
Prosty wykres pojedynczego dziedziczenia
Zwróć uwagę na postęp od ogólnego do określonego na rysunku. Innym typowym atrybutem występującym w projekcie większości hierarchii klas jest to, że klasa pochodna ma "rodzaj" relacji z klasą bazową. Na rysunku element Book
jest pewnym rodzajem PrintedDocument
, a element PaperbackBook
jest rodzajem book
.
Jeden inny element notatki na rysunku: Book
jest zarówno klasą pochodną (z PrintedDocument
) , jak i klasą bazową (PaperbackBook
pochodzi z Book
klasy ). W poniższym przykładzie pokazano deklarację szkieletową takiej hierarchii klas:
// deriv_SingleInheritance.cpp
// compile with: /LD
class PrintedDocument {};
// Book is derived from PrintedDocument.
class Book : public PrintedDocument {};
// PaperbackBook is derived from Book.
class PaperbackBook : public Book {};
PrintedDocument
uważa się za "bezpośrednią klasę bazową" do Book
; jest to "pośrednia klasa bazowa" do PaperbackBook
. Różnica polega na tym, że bezpośrednia klasa bazowa pojawia się na liście bazowej deklaracji klasy, a pośrednia baza nie.
Klasa bazowa, z której każda klasa jest pochodna, jest deklarowana przed deklaracją klasy pochodnej. Nie wystarczy podać deklaracji wyprzedzającej dla klasy bazowej; musi to być kompletna deklaracja.
W poprzednim przykładzie jest używany specyfikator public
dostępu. Znaczenie dziedziczenia publicznego, chronionego i prywatnego opisano w sekcji Kontrola dostępu do składowych.
Klasa może służyć jako klasa bazowa dla wielu określonych klas, jak pokazano na poniższej ilustracji.
Przykładowy graf skierowany acykliczny
Na powyższym diagramie, nazywanym "skierowanym grafem acyklicznym" (lub "DAG"), niektóre klasy są klasami bazowymi dla więcej niż jednej pochodnej. Jednak odwrotność nie jest prawdziwa: istnieje tylko jedna bezpośrednia klasa bazowa dla każdej danej klasy pochodnej. Wykres na rysunku przedstawia strukturę "pojedynczego dziedziczenia".
Uwaga
Grafy acykliczne nie są unikatowe dla pojedynczego dziedziczenia. Są one również używane do przedstawiania grafów wielokrotnego dziedziczenia.
W dziedziczeniu klasa pochodna zawiera składowe klasy bazowej oraz wszystkich nowych składowych, które dodajesz. W rezultacie klasa pochodna może odwoływać się do składowych klasy bazowej (chyba że te składowe są ponownie zdefiniowane w klasie pochodnej). Operator rozpoznawania zakresu (::
) może służyć do odwoływania się do składowych bezpośrednich lub pośrednich klas bazowych, gdy te składowe zostały ponownie zdefiniowane w klasie pochodnej. Rozważ taki przykład:
// deriv_SingleInheritance2.cpp
// compile with: /EHsc /c
#include <iostream>
using namespace std;
class Document {
public:
char *Name; // Document name.
void PrintNameOf(); // Print name.
};
// Implementation of PrintNameOf function from class Document.
void Document::PrintNameOf() {
cout << Name << endl;
}
class Book : public Document {
public:
Book( char *name, long pagecount );
private:
long PageCount;
};
// Constructor from class Book.
Book::Book( char *name, long pagecount ) {
Name = new char[ strlen( name ) + 1 ];
strcpy_s( Name, strlen(Name), name );
PageCount = pagecount;
};
Należy pamiętać, że konstruktor dla Book
(Book::Book
) ma dostęp do członka danych Name
. W programie można utworzyć obiekt typu Book
i użyć go w następujący sposób:
// Create a new object of type Book. This invokes the
// constructor Book::Book.
Book LibraryBook( "Programming Windows, 2nd Ed", 944 );
...
// Use PrintNameOf function inherited from class Document.
LibraryBook.PrintNameOf();
Jak pokazano w poprzednim przykładzie, dane i funkcje członkowskie oraz dziedziczone są używane identycznie. Jeśli implementacja klasy Book
wywołuje ponowne wdrożenie PrintNameOf
funkcji, funkcja, która należy do Document
klasy, może być wywoływana tylko za pomocą operatora rozpoznawania zakresu (::
):
// deriv_SingleInheritance3.cpp
// compile with: /EHsc /LD
#include <iostream>
using namespace std;
class Document {
public:
char *Name; // Document name.
void PrintNameOf() {} // Print name.
};
class Book : public Document {
Book( char *name, long pagecount );
void PrintNameOf();
long PageCount;
};
void Book::PrintNameOf() {
cout << "Name of book: ";
Document::PrintNameOf();
}
Wskaźniki i odwołania do klas pochodnych mogą być niejawnie konwertowane na wskaźniki i odwołania do ich klas bazowych, jeśli istnieje dostępna, jednoznaczna klasa bazowa. Poniższy kod demonstruje tę koncepcję przy użyciu wskaźników (ta sama zasada dotyczy odwołań):
// deriv_SingleInheritance4.cpp
// compile with: /W3
struct Document {
char *Name;
void PrintNameOf() {}
};
class PaperbackBook : public Document {};
int main() {
Document * DocLib[10]; // Library of ten documents.
for (int i = 0 ; i < 5 ; i++)
DocLib[i] = new Document;
for (int i = 5 ; i < 10 ; i++)
DocLib[i] = new PaperbackBook;
}
W poprzednim przykładzie tworzone są różne typy. Jednak ponieważ wszystkie te typy pochodzą z Document
klasy, istnieje niejawna konwersja na Document *
. W rezultacie jest to "lista heterogeniczna" (lista, DocLib
w której nie wszystkie obiekty są tego samego typu) zawierające różne rodzaje obiektów.
Document
Ponieważ klasa ma PrintNameOf
funkcję, może drukować nazwę każdej książki w bibliotece, chociaż może pominąć niektóre informacje specyficzne dla typu dokumentu (liczba stron dla Book
, liczba bajtów dla HelpFile
, itd.).
Uwaga
Wymuszanie klasy bazowej do implementacji funkcji, takiej jak PrintNameOf
, często nie jest najlepszym rozwiązaniem projektowym.
Usługa Virtual Functions oferuje inne alternatywy projektowe.