C + + language Learning (16)--Multiple inheritance

Source: Internet
Author: User

C + + language Learning (16)--Multiple inheritance One, multiple inheritance Introduction 1, Introduction to multiple inheritance

The C + + language supports multiple inheritance, a subclass can have more than one parent class, a subclass has member variables for all the parent classes, and the subclass inherits the member functions of all the parent classes, and the subclass object can be used as any parent class object.

2. Multiple Inheritance grammar rules
class Derived : public BaseA,                   public BaseB,                   public BaseC   {};
3. Multiple inheritance of the memory layout of derived classes

Derived class objects that are obtained through multiple inheritance may have different addresses.

#include <iostream>using namespace Std;class basea{public:basea (int a) {ma = A; }private:int Ma;};    Class Baseb{public:baseb (int b) {MB = b; }private:int MB;};    Class Derived:public basea,public baseb{public:derived (int A, int b, int c): Basea (a), Baseb (b) {mc = c; }private:int MC;};    struct test{int A;    int b; int C;};    int main (int argc, char *argv[]) {Derived d (n/a);    cout << sizeof (d) << ENDL;//12 test* p = (test*) &d; cout << p->a << endl;//1 cout << p->b << endl;//2 cout << p->c << Endl; 3 cout << &p->a << endl;//1 cout << &p->b << endl;//2 cout << &    P->c << ENDL;//3 basea* pa = &d;    baseb* PB = &d;    The address of the subclass object, the member address of the first inheriting class cout << &d << Endl;    cout << pa << Endl;    cout << &p->a <<endl; Sub-class object address, secondaryThe member address of the bit inheriting class cout << PB << Endl;    cout << &p->b << Endl; return 0;}

In the code above, the memory layout of the derived class object is as follows:

The member variables where the derived class objects inherit from the base class are arranged sequentially according to the inherited declaration order. Based on the assignment compatibility principle, if the Basea type pointer pa, BASEB type pointer PB all points to the subclass object D,pa will get the address of the Basea base class member variable MA, that is, the address of the subclass object; PB will get the address of the BASEB class member variable MB, therefore, the PA and PB address are not the same.

4, diamond Multi-inheritance caused by member redundancy


In the above class diagram, both the teacher class and the student class inherit people members, doctor inherit the members of the teacher class and the student class, so doctor will have two copies of the members inherited from the top-level parent class people.

#include <iostream> #include <string>using namespace Std;class people{public:people (string name, int age)        {m_name = name;    M_age = age; } void Print () {cout << "name:" << m_name << "Age:" << m_age <<    Endl    }private:string M_name; int m_age;};  Class Teacher:public People{public:teacher (string name, int age):P eople (name, age) {}};class student:public People{public:student (string name, int age):P eople (name, age) {}};class doctor:public Teacher, public studen T{public:doctor (string name, int age): Teacher (name + "_1", Age), Student (name + "_2", age) {}};i    NT Main (int argc, char *argv[]) {Doctor doc ("Bauer", 30);    Doc.print ();//error//error:request for member ' print ' is ambiguous//doctor inherits the print function inherited from Teacher,student. Doc.    People::p rint ();//error//error: ' People ' is an ambiguous base of ' Doctor '//people was inherited twiceDoc. Teacher::p rint ();//name:bauer_1 age:30 Doc. Student::p rint ();//name:bauer_2 age:30 return 0;}
Second, virtual inheritance 1, Virtual inheritance Introduction

In multi-inheritance, saving multiple copies of the same name in a common base class can store different data in different data members, but keeping copies of multiple data members not only occupies more storage space, increases the redundancy of members, but also increases the difficulty of access. C + + provides a virtual base class and virtual inheritance mechanism, which realizes that only one common member is retained in multiple inheritance.
C + + The solution to the redundancy problem caused by diamond multi-inheritance is to use virtual inheritance.
In virtual inheritance, the middle-tier parent is no longer concerned with the initialization of the top-level parent class, and the final subclass must call the constructor of the top-level parent class directly.
The syntax for virtual inheritance is as follows:
class 派生类名:virtual 继承方式 基类名

2. Virtual Inheritance Example
#include <iostream> #include <string>using namespace Std;class people{public:people (string name, int age)        {m_name = name;    M_age = age; } void Print () {cout << "name:" << m_name << "Age:" << m_age <<    Endl    }private:string M_name; int m_age;}; Class Teacher:virtual public People{public:teacher (string name, int.):P eople (name, age) {}};class Student : Virtual public people{public:student (string name, int.):P eople (name, age) {}};class doctor:public Teache R, Public student{public://The final subclass must call the constructor of the top-level parent class Doctor (string name, int age): People (name, age), Teacher (name + "_1", Age), Student (name + "_2", age) {}};int main (int argc, char *argv[]) {Doctor doc ("Bauer", 3    0); Doc.print ();//name:bauer age:30 Doc. People::p rint ();//name:bauer age:30 Doc. Teacher::p rint ();//name:bauer age:30 Doc. Student::p rint ();//name:bauer Age:return 0;} 

In the preceding code, using virtual inheritance solves the problem of member redundancy.
Virtual inheritance solves the problem of data redundancy resulting from multi-inheritance, but the middle-tier parent is no longer concerned with the initialization of the top-level parent class, and the final subclass must call the constructor of the top-level parent class directly.

Iii. multiple inheritance of derived classes object Model 1, multiple inheritance memory layout of derived class objects


In the above class diagram, the derived class inherits from the Basea and Baseb classes, Funca and FUNCB are virtual functions, and the derived object model is as follows:

#include <iostream> #include <string>using namespace Std;class basea{public:basea (int a) {m_a =    A    } virtual void Funca () {cout << "Basea::funca ()" <<endl; }private:int m_a;};    Class Baseb{public:baseb (int b) {m_b = b;    } virtual void Funcb () {cout << "BASEB::FUNCB ()" <<endl; }private:int M_b;};    Class Derived:public Basea, public baseb{public:derived (int a, int b, int c): Basea (a), Baseb (b) {m_c = C; }private:int M_c;};    struct test{void* Vptra;    int A;    void* VPTRB;    int b; int C;};    int main (int argc, char *argv[]) {cout << sizeof (Derived) << Endl;    Derived d (n/a);    test* pTest = (test*) &d; cout << ptest->a <<endl;//1 cout << ptest->b <<endl;//2 cout << ptest->c < &LT;ENDL;//3 cout << ptest->vptra <<endl;//cout << ptest->vptrb <<endl;//    return 0;} 
2. Diamond inherits the memory layout of the derived class object

The Diamond Inheritance sample code is as follows:

#include <iostream> #include <string>using namespace Std;class people{public:people (string name, int age)        {m_name = name;    M_age = age; } void Print () {cout << "name:" << m_name << "Age:" << m_age <<    Endl    }private:string M_name; int m_age;}; Class Teacher:public people{string M_research;public:teacher (string name, int age, String):P eople (name    + "_1", age + 1) {M_research =; }};class student:public people{string m_major;public:student (string name, int age,string major):P eople (name + "_    2 ", age + 2) {m_major = major; }};class doctor:public Teacher, public student{string M_subject;public:doctor (string name, int age,string resear CH, string major, String subject): Teacher (name, Age,research), Student (name, age, Major) {m_subject = Su    Bject;    }};struct test{string name1;    int age1; String REsearch;    String name2;    int age2;    String Major; string subject;};    int main (int argc, char *argv[]) {Doctor doc ("Bauer", "Computer", "Computer engneering", "HPC");    cout << "Doctor size:" << sizeof (DOC) << Endl;    test* pTest = (test*) &doc;    cout << ptest->name1 << Endl;    cout << ptest->age1 << Endl;    cout << ptest->research << Endl;    cout << ptest->name2 << Endl;    cout << ptest->age2 << Endl;    cout << ptest->major << Endl;    cout << ptest->subject << Endl; return 0;} output://Doctor size:28//bauer_1//31//computer//bauer_2//32//computer engneering//HPC

In the

above code, the memory of the underlying subclass object is as follows:

The underlying subclass object inherits member variables from the top-level parent class, respectively, so the memory model contains two member variables of the underlying parent class.
If the top-level parent class contains virtual functions, the middle-tier parent inherits the virtual function table pointers of the top-level parent class, and therefore the underlying subclass object memory layout is as follows:

#include <iostream> #include <string>using namespace Std;class people{public:people (string name, int age)        {m_name = name;    M_age = age; virtual void print () {cout << "name:" << m_name << "Age:" << m_age    <<endl;    }private:string M_name; int m_age;}; Class Teacher:public people{string M_research;public:teacher (string name, int age, String):P eople (name    + "_1", age + 1) {M_research =; }};class student:public people{string m_major;public:student (string name, int age,string major):P eople (name + "_    2 ", age + 2) {m_major = major; }};class doctor:public Teacher, public student{string M_subject;public:doctor (string name, int age,string resear CH, string major, String subject): Teacher (name, Age,research), Student (name, age, Major) {m_subject = Su    Bject; } virtual void print () {}};struct test{    void* vptr1;    String name1;    int age1;    string;    void* vptr2;    String name2;    int age2;    String Major; string subject;};    int main (int argc, char *argv[]) {Doctor doc ("Bauer", "Computer", "Computer engneering", "HPC");    cout << "Doctor size:" << sizeof (DOC) << Endl;    test* pTest = (test*) &doc;    cout << ptest->vptr1 << Endl;    cout << ptest->name1 << Endl;    cout << ptest->age1 << Endl;    cout << ptest->research << Endl;    cout << ptest->vptr2 << Endl;    cout << ptest->name2 << Endl;    cout << ptest->age2 << Endl;    cout << ptest->major << Endl;    cout << ptest->subject << Endl; return 0;} output://Doctor size:28//0x405370//bauer_1//31//computer//0x40537c//bauer_2//32//computer Engneering//HPC
3. Virtual inheritance memory layout of derived class objects

Virtual inheritance is a means to solve the problem of multiple inheritance of C + +, the underlying implementation principle of virtual inheritance is related to C + + compiler, generally through virtual base class pointers and virtual base class table implementation, each virtual inheriting subclass has a virtual base class pointer (occupies a pointer storage space, 4 (8) bytes) and the Virtual base class table (which does not occupy the storage space of the class object) (the virtual base class will still have copies in the subclass, but only one copy at most); Virtual base class pointers are also inherited when a virtual inheriting subclass is inherited as a parent class.
In the case of virtual inheritance, the layout of the underlying subclass object is different from normal inheritance, and a virtual base class table pointer vbptr is needed to point to the middle-tier parent class object.
Vbptr is a virtual base class table pointer (virtual base tables pointer), the VBPTR pointer points to a virtual base class table (virtual table), the virtual base class table stores the virtual base class relative directly inherits the class's offset address, through the offset address can find the virtual base class member, Virtual inheritance does not need to maintain a common base class (virtual base class) of two copies of the same copy, saving storage space.

#include <iostream> #include <string>using namespace Std;class people{public:people (string name, int age)        {m_name = name;    M_age = age;    } void Print () {cout << ' this: ' << this <<endl;    }private:string M_name; int m_age;}; Class Teacher:virtual Public people{string M_research;public:teacher (string name, Int. age, String):P Eop    Le (name + "_1", age + 1) {M_research =;    } void Print () {cout << ' this: ' << this <<endl; }};class student:virtual Public people{string m_major;public:student (string name, int age,string major):P eople (n    Ame + "_2", age + 2) {m_major = major;    } void Print () {cout << ' this: ' << this <<endl; }};class doctor:public Teacher, public student{string M_subject;public:doctor (string name, int age,string resear CH, string major, String subject): People (NamE, age), Teacher (name, Age,research), Student (name, age, Major) {m_subject = subject;    }};struct test{void* Vbptr_left;    string;    void* Vbptr_right;    String Major;    String subject;    String name; int age;};    int main (int argc, char *argv[]) {Doctor doc ("Bauer", "Computer", "Computer engneering", "HPC");    cout << "Doctor size:" << sizeof (DOC) << Endl;    test* pTest = (test*) &doc;    cout << ptest->vbptr_left << Endl;    cout << * (int*) ptest->vbptr_left << Endl;    cout << ptest->research << Endl;    cout << ptest->vbptr_right << Endl;    cout << * (int*) ptest->vbptr_right << Endl;    cout << ptest->major << Endl;    cout << ptest->subject << Endl;    cout << ptest->name << Endl;    cout << ptest->age << Endl; return 0;} output://Doctor size:28//0x40539c//12//computer//0x4053a8// 0//computer engneering//hpc//bauer//30 

The preceding code has no virtual function, and the memory layout of the underlying subclass object is as follows in the g++ compiler print result:

#include <iostream> #include <string>using namespace Std;class people{public:people (string name, int age)        {m_name = name;    M_age = age;    } virtual void print () {cout << ' this: ' << this <<endl;    }private:string M_name; int m_age;}; Class Teacher:virtual Public people{string M_research;public:teacher (string name, Int. age, String):P Eop    Le (name + "_1", age + 1) {M_research =;    } void Print () {cout << ' this: ' << this <<endl; } virtual void Func1 () {}};class student:virtual public people{string m_major;public:student (string name,    int age,string Major):P eople (name + "_2", age + 2) {m_major = major;    } void Print () {cout << ' this: ' << this <<endl; } virtual void Func2 () {}};class doctor:public Teacher, public student{string M_subject;public:doctor (stri ng name, int aGe,string, String major, String subject): People (name, age), Teacher (name, Age,research), Student (name, age,    Major) {m_subject = subject;    } void Print () {cout << ' this: ' << this <<endl;    } virtual void func3 () {}};struct test{void* vbptr_left;    char*;    void* Vbptr_right;    Char* Major;    char* subject;    void* vptr_base;    char* name; Long age;};    int main (int argc, char *argv[]) {Doctor doc ("Bauer", "Computer", "Computer engneering", "HPC");    cout << "Doctor size:" << sizeof (DOC) << Endl;    test* pTest = (test*) &doc;    cout << ptest->vbptr_left << Endl;    cout << Std::hex << * (int*) ptest->vbptr_left << Endl;    cout << std::d EC << * ((int*) ptest->vbptr_left+8) << Endl;    cout << std::d EC << * ((int*) ptest->vbptr_left+16) << Endl; cout << std::d EC << * ((int*) ptest->vbptr_left+24) << Endl;    cout << ptest->research << Endl;    cout << ptest->vbptr_right << Endl;    cout << ptest->major << Endl;    cout << ptest->subject << Endl;    cout << ptest->vptr_base << Endl;    cout << ptest->name << Endl;    cout << ptest->age << Endl; return 0;}

In the preceding code, virtual inheritance is used, so different implementations of the C + + compiler have different principles.
For the GCC compiler, the People object size is char + int + virtual function table pointer, teacher object size is char+ Virtual base class table pointer +a type size, student object size is char+ Virtual base class table pointer +a type size , the Doctor object size is char + int + virtual function table pointer +char+ Virtual base class table pointer +char+ Virtual base class table pointer +char*. The middle-tier parent class shares the virtual function table pointer of the top-level parent class, does not have its own virtual function table pointer, the virtual base class pointer is not shared, and therefore has its own independent virtual base class table pointer.
VC + +, GCC, and clang compiler implementations, whether or not virtual or virtual functions, the virtual base class pointers are not shared, are separate. For virtual function table pointers, the VC + + compiler determines whether virtual table pointers are shared in an inheritance relationship based on whether it is virtual inheritance. If a subclass is a virtual inheritance that has a virtual function parent, and the subclass has a newly added virtual function, a virtual function table pointer is added to the subclass, and the virtual function table pointers for the GCC compiler and the clang compiler are shared throughout the inheritance relationship.
The g++ compiler's memory distribution and virtual function table information commands for classes are as follows:

g++ -fdump-class-hierarchy main.cppcat main.cpp.002t.class

VC + + compiler for the memory distribution of the class and virtual function table information command as follows:
cl main.cpp /d1reportSingleClassLayoutX
The clang compiler's memory distribution and virtual function table information commands for classes are as follows:
clang -Xclang -fdump-record-layouts

4. virtual function table with multiple inheritance derived classes

All virtual functions are stored in a virtual function table, and multiple inheritance may produce multiple virtual function tables. In multiple inheritance, when a subclass overrides a virtual function of a parent class, the function of the subclass overrides the virtual function position of the parent class in the corresponding virtual function table, and when the child class has a new virtual function, the new virtual function is added to the end of the virtual function table of the first base class. When dynamic_cast a sub-class object, the child class and the first base class have the same address and do not need to move the pointer, but when dynamic_cast converts the subclass to another parent class, the corresponding pointer adjustment is required.

Four, multiple inherited pointer type conversion 1, multiple inheritance in the pointer type conversion trap

In the C + + language, it is common for a pointer to be type-converted, not to change the pointer's value, but only to change the pointer's type (that is, to change how the compiler interprets the pointer to memory), but it is not true in C + + multiple inheritance.

#include <iostream>using namespace Std;class basea{public:basea (int value = 0) {data = value;    } virtual void PrintA () {cout << "Basea::p rint data =" << data << Endl; }protected:int data;};    Class Baseb{public:baseb (int value = 0) {data = value;    } virtual void Printb () {cout << "BASEB::p rint data =" << data << Endl; }protected:int data;};    Class Derived:public Basea, public baseb{public:derived (int value = 0) {data = value;    } virtual void PrintA () {cout << "Derived printA data =" << data << Endl;    } virtual void Printb () {cout << "Derived printb data =" << data << Endl; }protected:int data;};    int main (int argc, char *argv[]) {derived* DPD = new Derived (102);    cout << DPD << endl;//0x8d1190 basea* BPA = (basea*) DPD; cout << BPA << endl;//0x8d1190 baseb* BPB = (baseb*) DPD; cout << BPB << endl;//0x8d1198 cout << (DPD = BPB) << endl;//1 return 0;}

In the preceding code, pointers to derived objects are converted to base classes Basea and Baseb , and the pointer values are not the same. The DPD pointer, the BPA pointer, and the BPB pointer have a 8-byte address space, which is the space occupied by the Basea class virtual function table pointer and the data member.
When a pointer to a derived class is converted to a base class pointer, the C + + compiler offsets the value of the pointer to the base class's starting position in the object's memory.

cout << (dpd == bpb) << endl;//1

The code above prints out that the 1,c++ compiler masks the difference of pointers, and when the C + + compiler encounters a pointer to a derived class and a pointer to one of its base classes for the = = operation, the pointer is automatically implicitly type promoted to mask the pointer differences brought by multiple inheritance.

2, multiple inheritance in the derived class, the base class pointer type conversion

When a derived class object pointer is converted to a different base class object pointer, the C + + compiler converts the pointer to the first base class in the order of inheritance declared by the derived class, and then offsets the number of bytes from the previous base class in turn.
Under multiple inheritance, pointer type conversions require consideration of this pointer adjustment problem.

Five, multiple inheritance application examples

In multi-inheritance, if the middle-tier parent class has more than two parent classes that implement virtual functions, it causes the subclasses to produce multiple virtual function table pointers, which can be used for type conversions using the dynamic_cast keyword.
In engineering practice, it is common to use single inheritance for a class and implement multiple interfaces to solve multiple inheritance problems.
code example:

#include <iostream> #include <string>using namespace Std;class base{protected:int mi;public:base (int i)    {mi = i;    } int Geti () {return mi;    } bool Equal (Base* obj) {return (this = = obj);    }};class interface1{public:virtual void Add (int i) = 0; virtual void minus (int i) = 0;};    Class interface2{public:virtual void Multiply (int i) = 0; virtual void divide (int i) = 0;}; Class Derived:public base, public Interface1, public interface2{public:derived (int i): base (i) {} void ad    d (int i) {mi + = i;    } void minus (int i) {mi-= i;    } void Multiply (int i) {mi *= i;        } void Divide (int i) {if (I! = 0) {mi/= i;    }}};int Main () {Derived d (100);    derived* p = &d;    interface1* pInt1 = &d;    interface2* PInt2 = &d;    cout << "p->geti () =" << P->geti () << Endl; Pint1-&gT;add (10);    Pint2->divide (11);    Pint1->minus (5);    Pint2->multiply (8);    cout << "p->geti () =" << P->geti () << Endl;    cout << Endl;    cout << "pInt1 = = P:" << p->equal (dynamic_cast<base*> (pInt1)) << Endl;    cout << "PInt2 = = P:" << p->equal (dynamic_cast<base*> (pInt2)) << Endl; return 0;}

It is best not to have multiple inheritance in the program design, but also to inherit multiple as interfaces using abstract classes (declaring only the required functionality, no specific implementation). Because the general multi-inheritance itself is a bad object-oriented programming.

C + + language Learning (16)--Multiple inheritance

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.