Udostępnij przez


Pojedyncze dziedziczenie

W przypadku "pojedynczego dziedziczenia" typowa forma dziedziczenia klasy mają tylko jedną klasę bazową. Rozważmy relację pokazaną na poniższym rysunku.

Diagram of a basic single inheritance hierarchy.
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 Bookklasy ). 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 PaperbackBookklasy . 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.

Diagram showing an inheritance hierarchy as a directed acyclic graph.
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 Bookelementu , (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.