AlgoMaster Logo

Types of Inheritance

High Priority11 min readUpdated June 6, 2026
Listen to this chapter
Unlock Audio

C++ allows one class to inherit from another in several different shapes. The rules of inheritance are always the same, but the structure of "who derives from whom" changes the design. This lesson surveys the five common shapes: single, multilevel, hierarchical, multiple, and hybrid inheritance. Each shape gets a class diagram, a short executable example built from the same product/cart/order domain, and a sentence on when that shape shows up in real code.

Two of the five shapes (multiple and hybrid) get their own dedicated chapters next, so this lesson keeps them at preview level. By the end of this lesson, any class hierarchy can be classified by shape.

Why the Shape Matters

Before getting into the shapes, it helps to be clear about what's being named. Every inheritance relationship in C++ is the same mechanic: a derived class gets the public and protected members of its base class, and an object of the derived type "is a" base. What changes between the five shapes is the overall structure of those relationships across multiple classes.

The shape matters for three reasons. First, it determines how constructors and destructors run, because longer chains mean more constructor calls. Second, it determines whether ambiguity is possible, because some shapes (like multiple inheritance) introduce name conflicts that simpler shapes never have. Third, it shapes how the design reads: a long single chain says "specialization", while a wide hierarchical layout says "variants of one concept". Naming the shape early makes the rest of the design conversation easier.

The running example uses a small slice of an online store: a base Product class and several kinds of products (digital downloads, physical goods, subscriptions). Each shape uses the same domain so the focus stays on the structure, not on learning a new example each time.

The five labels above are the five shapes this lesson covers, in the order they appear. They progress from the simplest (one base, one derived) to the most complex (combinations that produce the diamond pattern).

Single Inheritance

Single inheritance is the simplest shape: one base class, one derived class. The derived class gets everything public and protected from the base, then adds its own members on top. This is by far the most common shape in C++ codebases, and it's the one covered in the earlier lessons of this section.

The arrow with the open triangle points from the derived class to the base. Read it as "DigitalProduct is a Product".

The same shape in code. DigitalProduct adds a download URL and size on top of the base Product fields, and gains a new download() method, while still being usable wherever a Product is expected.

The ebook object has all four fields (name, price, downloadUrl, sizeMB) and both functions. The base members come from Product, the new ones come from DigitalProduct, and they coexist in one object. Single inheritance shows up whenever an existing type needs new capability without restructuring the rest of the codebase.

Multilevel Inheritance

Multilevel inheritance is single inheritance extended into a chain of three or more classes. Each link in the chain inherits from the one above it, and the bottom class transitively gets everything from every ancestor. Think of it as "B is an A, C is a B, so C is also an A".

Reading top to bottom: Product is the most general, DigitalProduct is a specialization of Product, and Ebook is a specialization of DigitalProduct. An Ebook object has every member from all three classes.

The code mirrors the diagram. Ebook doesn't list Product in its base; it inherits from DigitalProduct, which already inherits from Product. The chain handles the rest.

The book object contains six fields and three methods, sourced from three different classes, all reachable through one variable. The constructor order, when constructors are defined, runs from the most-base class downward: Product first, then DigitalProduct, then Ebook. Destructors run in the reverse order.

Each link in the chain adds another constructor and destructor call when the object is built and torn down. For a three-level chain, that's three pairs of calls per object, though the cost is usually small unless the constructors do real work.

Multilevel inheritance shows up in libraries that model "general kind, specific kind, very specific kind" relationships: an exception hierarchy where runtime_error derives from exception and a custom DatabaseError derives from runtime_error is a common case. Avoid overdoing the chain; a four- or five-level deep hierarchy becomes hard to follow.

Hierarchical Inheritance

Hierarchical inheritance is when one base class has many siblings deriving from it. Instead of going deeper, the tree fans out. Each derived class is a separate variant of the base, with its own additional members, but all of them share the base's common contract.

All three children inherit from the same Product base, but they're independent of each other. A DigitalProduct is not a PhysicalProduct; they're siblings, not a chain.

The code below defines all three siblings and uses them side by side in main.

All three derived classes share the print() behavior from Product but specialize differently. This is the right shape when there's a single concept (a product, a payment method, a shipping option) with several distinct variants that don't need to know about each other.

Hierarchical inheritance pairs naturally with polymorphism. If print() were virtual, all three could be stored in a std::vector<Product*> and print() called on each without knowing the variant.

Multiple Inheritance (Preview)

Multiple inheritance is a derived class with two or more base classes. The derived class inherits members from each base, and an object of the derived type "is a" each of its bases. C++ supports this directly, unlike Java or C#, which restrict it to a single class plus interfaces.

BundleOffer derives from both Product and Discountable. The arrows go from the derived class up to each base.

A small example of the syntax: list each base after the colon, separated by commas, with its own access specifier.

holiday has access to name and price from Product, discountPercent and applyDiscount from Discountable, and itemCount and describe from BundleOffer. Inside describe, the call to applyDiscount(price) works because the function comes from one base and the data comes from the other; both are part of the same object.

This is just a preview. Multiple inheritance becomes interesting when both bases have a member with the same name, or when used for mixin-style composition. For this survey, the takeaway is that the syntax is a comma-separated list of bases, and an object gets the members of all of them.

Hybrid Inheritance and the Diamond (Preview)

Hybrid inheritance combines the previous shapes in the same hierarchy. The most famous combination is the diamond: one common base, two intermediate classes that each inherit from it, and one bottom class that inherits from both intermediates. Drawn out, the four classes form a diamond shape.

The diamond is the canonical hybrid case: a class that picks up the same ancestor through two different paths. Without special handling, a DiscountedEbook ends up with two separate copies of the Product part, one through each path.

A short code sketch shows the shape, with the warning that this version still carries two copies of Product.

The book.DigitalProduct::name and book.Discountable::name syntax tells the compiler which copy of the inherited member is meant. Writing just book.name = "Modern C++" produces a compiler ambiguity error, because there's no single name to assign to. This is the diamond pattern in its raw form, and it's the situation that the next two chapters work through. The first explains why the duplication happens, and the second introduces virtual inheritance as the standard fix.

For now, the takeaway: any inheritance hierarchy that mixes the earlier shapes (multiple plus multilevel, in particular) is "hybrid", and any time two of those paths converge on a common ancestor, the result is a diamond.

Comparing the Five Shapes

The table below summarizes the five shapes, with the diagram pattern, where the shape tends to show up, and the main thing to watch out for. Use it as a quick reference when reading or designing a class hierarchy.

NameDiagram shapeTypical usePitfalls to watch
SingleTwo boxes, one arrowAdding capability to an existing typeHierarchies grow over time, so plan for change
MultilevelVertical chain of three or moreModeling "general, specific, very specific" relationshipsLong chains become hard to follow; favor flatter designs when possible
HierarchicalOne base, multiple siblings under itVariants of one concept that share a common contractVariants drift apart over time; the base contract has to stay shared
MultipleOne derived, two or more basesCombining unrelated capabilitiesName conflicts between bases require disambiguation
Hybrid/DiamondMultiple plus multilevel convergingCombining shapes; the diamond is the classic caseCommon ancestor gets duplicated; virtual inheritance fixes the diamond

The first three shapes (single, multilevel, hierarchical) are the everyday ones that appear in most C++ code. Multiple inheritance shows up less often but is useful for certain patterns. Hybrid hierarchies usually arise by accident as a codebase grows, and the diamond in particular is a signal to step back and ask whether the design is right or whether virtual inheritance is needed.

Design tip: When designing from scratch, prefer single or hierarchical inheritance over the more complex shapes. Multiple inheritance solves real problems, but it's also easy to misuse, and many C++ style guides require justification before using it.

A useful mental check: when reading a class hierarchy, name the shape. When the shape can't be named, the design probably has too many moving parts and would benefit from being simplified before any new feature is added.

A Cart That Holds Multiple Shapes

A small example that uses single and hierarchical inheritance side by side. The cart stores plain Product pointers but actually holds two different derived types. This is a hint at runtime polymorphism.

Three points apply. First, the cart's element type is Product*, but the actual objects pointed to are derived types. Second, calling item->show() calls the base function, because show() isn't virtual. With virtual, each derived class could provide its own show() and the cart would call the right one for each item. Third, the sizeMB and weightKg fields are not reachable through Product* even though the underlying objects have them; reaching derived-only members through a base pointer requires a cast or a redesign. Both of those, the virtual dispatch and the casting, are covered in the polymorphism section.

The cart-of-base-pointers pattern is the natural payoff of hierarchical inheritance: one collection holds many variants of the same concept, and code that processes the collection works against the common contract.

Quiz

Types of Inheritance Quiz

10 quizzes