Pojedyncze dziedziczenie
W przypadku "pojedynczego dziedziczenia" typowa forma 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 jest Book
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
jest traktowana jako "bezpośrednia klasa podstawowa" do Book
; jest to "pośrednia klasa bazowa" do PaperbackBook
klasy . 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ć deklarację odwołania do przodu dla klasy bazowej; musi być kompletną deklaracją.
W poprzednim przykładzie jest używany specyfikator public
dostępu. Znaczenie dziedziczenia publicznego, chronionego i prywatnego opisano w temacie 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 wykres Acykliczny skierowany
Na powyższym diagramie nazywanym "grafem acyklicznym" (lub "DAG"), niektóre klasy są klasami bazowymi dla więcej niż jednej klasy 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żmy następujący 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
elementu , (Book::Book
) ma dostęp do elementu członkowskiego 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 dziedziczone przez klasę 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 implementacji funkcji, takiej jak PrintNameOf
klasa bazowa, często nie jest najlepszym projektem. Usługa Virtual Functions oferuje inne alternatywy projektowe.
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla