1. Multiple Inheritance
class Bear : public ZooAnimal { }; |
The constructor initializer controls only the values that are used to initialize the base classes, not the order in which the base classes are constructed. The base-class constructors are invoked in the order in which they appear in the class derivation list. For Panda , the order of base-class initialization is:
- ZooAnimal, the ultimate base class up the hierarchy from Panda ‘s immediate base clas Bear.
- Bear, the first immediate base class.
- Endangered, the second immediate base, which itself has no base class.
- Panda, the members of Panda itself are initialized, and then the body of its constructor is run.
Destructors are always invoked in the reverse order from which the constructors are run. In our example, the order in which the destructors are called is ~Panda, ~Endangered, ~Bear, ~ZooAnimal.class X { ... };
class A { ... };
class B : public A { ... };
class C : private B { ... };
class D : public X, public C { ... };
which, if any, of the following conversions are not permitted?
D *pd = new D;
(a) X *px = pd; (c) B *pb = pd;
(b) A *pa = pd; (d) C *pc = pd;
b和c是错误的,因为C对B是私有继承的,对于C的子类而言,它完全不知道有B的存在,所有无法转化为B和A。
As is the case for single inheritance, if a class with multiple bases defines its own destructor, that destructor is responsible only for cleaning up the derived class. If the derived class defines its own copy constructor or assignment operator, then the class is responsible for copying (assigning) all the base class subparts. The base parts are automatically copied or assigned only if the derived class uses the synthesized versions of these members.
As usual, name lookup for a name used in a member function starts in the function itself. If the name is not found locally, then lookup continues in the member’s class and then searches each base class in turn. Under multiple inheritance, the search simultaneously examines all the base-class inheritance subtreesin our example, both the Endangered and the Bear / ZooAnimal subtrees are examined in parallel. If the name is found in more than one subtree, then the use of that name must explicitly specify which base class to use. Otherwise, the use of the name is ambiguous.
2. Multiple Base Classes Can Lead to Ambiguities
Assume both Bear and Endangered define a member named print . If Panda does not define that member, then a statement such as the following ying_yang.print(cout);
results in a compile-time error. Although the ambiguity of the two inherited print members is reasonably obvious, it might be more surprising to learn that an error would be generated even if the two inherited functions had
different parameter lists. Similarly, it would be an error even if the print function were private in one class and public or protected in the other. Finally, if print were defined in ZooAnimal and not Bear, the call would still be in error. It’s because name lookup happens first. As always, name lookup happens in two steps: First the compiler finds a matching declaration (or, in this case, two matching declarations, which causes the ambiguity). Only then does the compiler decide whether the declaration it found is legal.
We could resolve the print ambiguity by specifying which class to use:ying_yang.Endangered::print(cout);
. The best way to avoid potential ambiguities is to define a version of the function in the derived class that resolves the ambiguity.
3. Virtual Inheritance
class istream : public virtual ios { ... }; |
Members in the shared virtual base can be accessed unambiguously and directly. Similarly, if a member from the virtual base is redefined along only one derivation path, then that redefined member can be accessed directly. Under a nonvirtual derivation, both kinds of access would be ambiguous. Assume a member named X is inherited through more than one derivation path. There are three
possibilities:
- If in each path X represents the same virtual base class member, then there is no ambiguity because a single instance of the member is shared.
- If in one path X is a member of the virtual base class member and in another path X is a member of a subsequently derived class, there is also no ambiguitythe specialized derived class instance is given precedence over the shared virtual base class instance.
- If along each inheritance path X represents a different member of a subsequently derived class, then the direct access of the member is ambiguous.
4. Special Initialization Semantics
To solve the duplicate-initialization problem, classes that inherit from a class that has a virtual base have special handling for initialization. In a virtual derivation, the virtual base is initialized by the most derived constructor. Although the virtual base is initialized by the most derived class, any classes that inherit immediately or indirectly from the virtual base usually also have to provide their own initializers for that base. As long as we can create independent objects of a type derived from a virtual base, that class must initialize its virtual base. These initializers are used only when we create objects of the intermediate type.Panda::Panda(std::string name, bool onExhibit) : ZooAnimal(name, onExhibit, "Panda"),
Bear(name, onExhibit),
Raccoon(name, onExhibit),
Endangered(Endangered::critical),
sleeping_flag(false) { }
When a Panda object is created:
- The ZooAnimal part is constructed first, using the initializers specified in the Panda constructor initializer list.
- Next, the Bear part is constructed. The initializers for ZooAnimal Bear’s constructor initializer list are ignored.
- Then the Raccoon part is constructed, again ignoring the ZooAnimal initializers.
- Finally, the Panda part is constructed.
If the Panda constructor does not explicitly initialize the ZooAnimal base class, then the ZooAnimal default constructor is used. If ZooAnimal doesn’t have a default constructor, then the code is in error.
5. Constructor and Destructor Order
Virtual base classes are always constructed prior to nonvirtual base classes regardless of where they appear in the inheritance hierarchy.
The immediate base classes are examined in declaration order to determine whether there are any virtual base classes. In our example, the inheritance subtree of BookCharacter is examined first, then that of Bear , and finally that of ToyAnimal. Each subtree is examined starting at the root class down to the most derived class.
The order in which the virtual base classes are constructed for TeddyBear is ZooAnimal followed by ToyAnimal. Once the virtual base classes are constructed, the nonvirtual base-class constructors are invoked in declaration order: BookCharacter, which causes the Character constructor to be invoked, and then Bear. Thus, to create a TeddyBear, the constructors are invoked in the following order:ZooAnimal(); // Bear's virtual base class
ToyAnimal(); // immediate virtual base class
Character(); // BookCharacter's nonvirtual base class
BookCharacter(); // immediate nonvirtual base class
Bear(); // immediate nonvirtual base class
TeddyBear(); // most derived class
where the initializers used for ZooAnimal and ToyAnimal are specified by the most derived class TeddyBear. The same construction order is used in the synthesized copy constructor; the base classes also are assigned in this order in the synthesized assignment operator. The order of base-class destructor calls is guaranteed to be the reverse order of constructor invocation.
6. example1
class Class { ... }; |
What is the order of constructor and destructor for the definition of a Final object?Class()
Base()
Derived1()
Derived2()
MI()
Class()
Final()
将首先调用虚基类Base的构造函数(导致调用Class())。
7. example2
class Base { |
注意,任何直接或间接继承虚基类Base的类都要必须为Base类提供显示初始化式(例如MI、Final),否则将无法创建相应类的对象。而且编译的时候还会有警告warning: direct base ‘Class’ inaccessible in ‘Final’ due to ambiguity