-1- A list of base classes can be specified in a class definition using the notation:
base-clause: : base-specifier-list
base-specifier-list: base-specifier base-specifier-list , base-specifier
base-specifier: ::opt nested-name-specifieropt class-name virtual access-specifieropt ::opt nested-name-specifieropt class-name access-specifier virtualopt ::opt nested-name-specifieropt class-name
The class-name in a base-specifier shall not be an incompletely defined class (clause class); this class is called a direct base class for the class being declared. During the lookup for a base class name, non-type names are ignored (basic.scope.hiding). If the name found is not a class-name, the program is ill-formed. A class B is a base class of a class D if it is a direct base class of D or a direct base class of one of D's base classes. A class is an indirect base class of another if it is a base class but not a direct base class. A class is said to be (directly or indirectly) derived from its (direct or indirect) base classes. [Note: See clause class.access for the meaning of access-specifier. ] Unless redefined in the derived class, members of a base class are also considered to be members of the derived class. The base class members are said to be inherited by the derived class. Inherited members can be referred to in expressions in the same manner as other members of the derived class, unless their names are hidden or ambiguous (class.member.lookup). [Note: the scope resolution operator :: (expr.prim) can be used to refer to a direct or indirect base member explicitly. This allows access to a name that has been redefined in the derived class. A derived class can itself serve as a base class subject to access control; see class.access.base. A pointer to a derived class can be implicitly converted to a pointer to an accessible unambiguous base class (conv.ptr). An lvalue of a derived class type can be bound to a reference to an accessible unambiguous base class (dcl.init.ref). ]access-specifier: private protected public
-2- The base-specifier-list specifies the type of the base class subobjects contained in an object of the derived class type. [Example:
class Base { public: int a, b, c; };
class Derived : public Base { public: int b; };
Here, an object of class Derived2 will have a sub-object of class Derived which in turn will have a sub-object of class Base. ]class Derived2 : public Derived { public: int c; };
-3- The order in which the base class subobjects are allocated in the most derived object (intro.object) is unspecified. [Note: a derived class and its base class sub-objects can be represented by a directed acyclic graph (DAG) where an arrow means ``directly derived from.'' A DAG of sub-objects is often referred to as a ``sub-object lattice.'' [ROFF DIAGRAM OMITTED] The arrows need not have a physical representation in memory. ]
-4- [Note: initialization of objects representing base classes can be specified in constructors; see class.base.init. ]
-5-
[Note:
A base class subobject might have a layout (basic.stc)
different from the layout of a most derived object of the same type.
A base class subobject might have a polymorphic behavior (class.cdtor)
different from the polymorphic behavior of a most derived object of the same
type.
A base class subobject may be of zero size (clause class);
however, two subobjects that have the same class type and that belong to the
same most derived object must not be allocated at the same address
(expr.eq).
]
10.1 - Multiple base classes [class.mi]
-1- A class can be derived from any number of base classes. [Note: the use of more than one direct base class is often called multiple inheritance. ] [Example:
class A { /* ... */ }; class B { /* ... */ }; class C { /* ... */ }; class D : public A, public B, public C { /* ... */ };
-2- [Note: the order of derivation is not significant except as specified by the semantics of initialization by constructor (class.base.init), cleanup (class.dtor), and storage layout (class.mem, class.access.spec). ]
-3- A class shall not be specified as a direct base class of a derived class more than once. [Note: a class can be an indirect base class more than once and can be a direct and an indirect base class. There are limited things that can be done with such a class. The non-static data members and member functions of the direct base class cannot be referred to in the scope of the derived class. However, the static members, enumerations and types can be unambiguously referred to. ] [Example:
class X { /* ... */ }; class Y : public X, public X { /* ... */ }; //ill-formed
class L { public: int next; /* ... */ }; class A : public L { /* ... */ }; class B : public L { /* ... */ }; class C : public A, public B { void f(); /* ... */ }; //well-formed class D : public A, public L { void f(); /* ... */ }; // well-formed
-4- A base class specifier that does not contain the keyword virtual, specifies a nonvirtual base class. A base class specifier that contains the keyword virtual, specifies a virtual base class. For each distinct occurrence of a nonvirtual base class in the class lattice of the most derived class, the most derived object (intro.object) shall contain a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type. [Example: for an object of class type C, each distinct occurrence of a (non-virtual) base class L in the class lattice of C corresponds one-to-one with a distinct L subobject within the object of type C. Given the class C defined above, an object of class C will have two sub-objects of class L as shown below. [ROFF DIAGRAM OMITTED] In such lattices, explicit qualification can be used to specify which subobject is meant. The body of function C::f could refer to the member next of each L subobject:
Without the A:: or B:: qualifiers, the definition of C::f above would be ill-formed because of ambiguity (class.member.lookup).void C::f() { A::next = B::next; } //well-formed
-5- For another example,
for an object c of class type C, a single subobject of type V is shared by every base subobject of c that is declared to have a virtual base class of type V. Given the class C defined above, an object of class C will have one subobject of class V, as shown below. [ROFF DIAGRAM OMITTED]class V { /* ... */ }; class A : virtual public V { /* ... */ }; class B : virtual public V { /* ... */ }; class C : public A, public B { /* ... */ };
-6- A class can have both virtual and nonvirtual base classes of a given type.
For an object of class AA, all virtual occurrences of base class B in the class lattice of AA correspond to a single B subobject within the object of type AA, and every other occurrence of a (non-virtual) base class B in the class lattice of AA corresponds one-to-one with a distinct B subobject within the object of type AA. Given the class AA defined above, class AA has two sub-objects of class B: Z's B and the virtual B shared by X and Y, as shown below. [ROFF DIAGRAM OMITTED]class B { /* ... */ }; class X : virtual public B { /* ... */ }; class Y : virtual public B { /* ... */ }; class Z : public B { /* ... */ }; class AA : public X, public Y, public Z { /* ... */ };
-1- Member name lookup determines the meaning of a name (id-expression) in a class scope (basic.scope.class). Name lookup can result in an ambiguity, in which case the program is ill-formed. For an id-expression, name lookup begins in the class scope of this; for a qualified-id, name lookup begins in the scope of the nested-name-specifier. Name lookup takes place before access control (basic.lookup, clause class.access).
-2- The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. A member name f in one sub-object B hides a member name f in a sub-object A if A is a base class sub-object of B. Any declarations that are so hidden are eliminated from consideration. Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration.*
[Footnote: Note that using-declarations cannot be used to resolve inherited member ambiguities; see namespace.udecl. --- end foonote]If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.
-3- [Example:
class A { public: int a; int (*b)(); int f(); int f(int); int g(); };
class B { int a; int b(); public: int f(); int g; int h(); int h(int); };
class C : public A, public B {};
void g(C* pc) { pc->a = 1; //error: ambiguous: A::a or B::a pc->b(); // error: ambiguous: A::b or B::b pc->f(); // error: ambiguous: A::f or B::f pc->f(1); // error: ambiguous: A::f or B::f pc->g(); // error: ambiguous: A::g or B::g pc->g = 1; // error: ambiguous: A::g or B::g pc->h(); // OK pc->h(1); // OK }
struct U { static int i; }; struct V : U { }; struct W : U { using U::i; }; struct X : V, W { void foo(); }; void X::foo() { i; //finds U::i in two ways: as W::i and U::i in V // no ambiguity because U::i is static }
-4- If the name of an overloaded function is unambiguously found, overloading resolution (over.match) also takes place before access control. Ambiguities can often be resolved by qualifying a name with its class name. [Example:
class A { public: int f(); };
class B { public: int f(); };
class C : public A, public B { int f() { return A::f() + B::f(); } };
-5- A static member, a nested type or an enumerator defined in a base class T can unambiguously be found even if an object has more than one base class subobject of type T. Two base class subobjects share the nonstatic member subobjects of their common virtual base classes. [Example:
class V { public: int v; }; class A { public: int a; static int s; enum { e }; }; class B : public A, public virtual V {}; class C : public A, public virtual V {};
class D : public B, public C { }; void f(D* pd) { pd->v++; //OK: only one v (virtual) pd->s++; // OK: only one s (static) int i = pd->e; // OK: only one e (enumerator) pd->a++; // error, ambiguous: two as in D }
-6- When virtual base classes are used, a hidden declaration can be reached along a path through the sub-object lattice that does not pass through the hiding declaration. This is not an ambiguity. The identical use with nonvirtual base classes is an ambiguity; in that case there is no unique instance of the name that hides all the others. [Example:
class V { public: int f(); int x; }; class W { public: int g(); int y; }; class B : public virtual V, public W { public: int f(); int x; int g(); int y; }; class C : public virtual V, public W { };
[ROFF DIAGRAM OMITTED] The names defined in V and the left hand instance of W are hidden by those in B, but the names defined in the right hand instance of W are not hidden at all.class D : public B, public C { void glorp(); };
void D::glorp() { x++; //OK: B::x hides V::x f(); // OK: B::f() hides V::f() y++; // error: B::y and C's W::y g(); // error: B::g() and C's W::g() }
-7- An explicit or implicit conversion from a pointer to or an lvalue of a derived class to a pointer or reference to one of its base classes shall unambiguously refer to a unique object representing the base class. [Example:
class V { }; class A { }; class B : public A, public virtual V { }; class C : public A, public virtual V { }; class D : public B, public C { };
void g() { D d; B* pb = &d; A* pa = &d; //error, ambiguous: C's A or B's A? V* pv = &d; // OK: only one V sub-object }
-1- Virtual functions support dynamic binding and object-oriented programming. A class that declares or inherits a virtual function is called a polymorphic class.
-2- If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name and same parameter list as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides*
[Footnote: A function with the same name but a different parameter list (clause over) as a virtual function is not necessarily virtual and does not override. The use of the virtual specifier in the declaration of an overriding function is legal but redundant (has empty semantics). Access control (clause class.access) is not considered in determining overriding. --- end foonote]Base::vf. For convenience we say that any virtual function overrides itself. Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (class.member.lookup) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations. [Example:
struct A { virtual void f(); }; struct B : virtual A { virtual void f(); };
struct C : B , virtual A { using A::f; }; void foo() { C c; c.f(); //calls B::f, the final overrider c.C::f(); // calls A::f because of the using-declaration }
-3- [Note: a virtual member function does not have to be visible to be overridden, for example,
the function f(int) in class D hides the virtual function f() in its base class B; D::f(int) is not a virtual function. However, f() declared in class D2 has the same name and the same parameter list as B::f(), and therefore is a virtual function that overrides the function B::f() even though B::f() is not visible in class D2. ]struct B { virtual void f(); }; struct D : B { void f(int); }; struct D2 : D { void f(); };
-4- Even though destructors are not inherited, a destructor in a derived class overrides a base class destructor declared virtual; see class.dtor and class.free.
-5- The return type of an overriding function shall be either identical to the return type of the overridden function or covariant with the classes of the functions. If a function D::f overrides a function B::f, the return types of the functions are covariant if they satisfy the following criteria:
[Footnote: Multi-level pointers to classes or references to multi-level pointers to classes are not allowed. --- end foonote]
class B {}; class D : private B { friend class Derived; }; struct Base { virtual void vf1(); virtual void vf2(); virtual void vf3(); virtual B* vf4(); virtual B* vf5(); void f(); };
struct No_good : public Base { D* vf4(); //error: B (base class of D) inaccessible };
class A; struct Derived : public Base { void vf1(); //virtual and overrides Base::vf1() void vf2(int); // not virtual, hides Base::vf2() char vf3(); // error: invalid difference in return type only D* vf4(); // OK: returns pointer to derived class A* vf5(); // error: returns pointer to incomplete class void f(); };
void g() { Derived d; Base* bp = &d; //standard conversion: // Derived* to Base* bp->vf1(); // calls Derived::vf1() bp->vf2(); // calls Base::vf2() bp->f(); // calls Base::f() (not virtual) B* p = bp->vf4(); // calls Derived::pf() and converts the // result to B* Derived* dp = &d; D* q = dp->vf4(); // calls Derived::pf() and does not // convert the result to B* dp->vf2(); // ill-formed: argument mismatch }
-6- [Note: the interpretation of the call of a virtual function depends on the type of the object for which it is called (the dynamic type), whereas the interpretation of a call of a nonvirtual member function depends only on the type of the pointer or reference denoting that object (the static type) (expr.call). ]
-7- [Note: the virtual specifier implies membership, so a virtual function cannot be a nonmember (dcl.fct.spec) function. Nor can a virtual function be a static member, since a virtual function call relies on a specific object for determining which function to invoke. A virtual function declared in one class can be declared a friend in another class. ]
-8- A virtual function declared in a class shall be defined, or declared pure (class.abstract) in that class, or both; but no diagnostic is required (basic.def.odr).
-9- [Example: here are some uses of virtual functions with multiple base classes:
struct A { virtual void f(); };
struct B1 : A { //note non-virtual derivation void f(); };
struct B2 : A { void f(); };
struct D : B1, B2 { //D has two separate A sub-objects };
In class D above there are two occurrences of class A and hence two occurrences of the virtual member function A::f. The final overrider of B1::A::f is B1::f and the final overrider of B2::A::f is B2::f.void foo() { D d; // A* ap = &d; //would be ill-formed: ambiguous B1* b1p = &d; A* ap = b1p; D* dp = &d; ap->f(); // calls D::B1::f dp->f(); // ill-formed: ambiguous }
-10- The following example shows a function that does not have a unique final overrider:
struct A { virtual void f(); };
struct VB1 : virtual A { //note virtual derivation void f(); };
struct VB2 : virtual A { void f(); };
struct Error : VB1, VB2 { //ill-formed };
Both VB1::f and VB2::f override A::f but there is no overrider of both of them in class Error. This example is therefore ill-formed. Class Okay is well formed, however, because Okay::f is a final overrider.struct Okay : VB1, VB2 { void f(); };
-11- The following example uses the well-formed classes from above.
struct VB1a : virtual A { //does not declare f };
struct Da : VB1a, VB2 { };
void foe() { VB1a* vb1ap = new Da; vb1ap->f(); //calls VB2::f }
-12- Explicit qualification with the scope operator (expr.prim) suppresses the virtual call mechanism. [Example:
Here, the function call in D::f really does call B::f and not D::f. ]class B { public: virtual void f(); }; class D : public B { public: void f(); }; void D::f() { /* ... */ B::f(); }
-1- The abstract class mechanism supports the notion of a general concept, such as a shape, of which only more concrete variants, such as circle and square, can actually be used. An abstract class can also be used to define an interface for which derived classes provide a variety of implementations.
-2- An abstract class is a class that can be used only as a base class of some other class; no objects of an abstract class can be created except as sub-objects of a class derived from it. A class is abstract if it has at least one pure virtual function. [Note: such a function might be inherited: see below. ] A virtual function is specified pure by using a pure-specifier (class.mem) in the function declaration in the class declaration. A pure virtual function need be defined only if explicitly called with the qualified-id syntax (expr.prim). [Example:
class point { /* ... */ }; class shape { //abstract class point center; // ... public: point where() { return center; } void move(point p) { center=p; draw(); } virtual void rotate(int) = 0; // pure virtual virtual void draw() = 0; // pure virtual // ... };
struct C { virtual void f() { }=0; //ill-formed };
-3- An abstract class shall not be used as a parameter type, as a function return type, or as the type of an explicit conversion. Pointers and references to an abstract class can be declared. [Example:
shape x; //error: object of abstract class shape* p; // OK shape f(); // error void g(shape); // error shape& h(shape&); // OK
-4- A class is abstract if it contains or inherits at least one pure virtual function for which the final overrider is pure virtual. [Example:
Since shape::draw() is a pure virtual function ab_circle::draw() is a pure virtual by default. The alternative declaration,class ab_circle : public shape { int radius; public: void rotate(int) {} //ab_circle::draw() is a pure virtual };
would make class circle nonabstract and a definition of circle::draw() must be provided. ]class circle : public shape { int radius; public: void rotate(int) {} void draw(); //a definition is required somewhere };
-5- [Note: an abstract class can be derived from a class that is not abstract, and a pure virtual function may override a virtual function which is not pure. ]
-6- Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (class.virtual) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.