15- C++ Inheritance-3 (C++)

Chapter IV Inheritance

4.1 Why is inheritance needed?

Inheritance is a very natural concept, and many things in the real world are inherited. People generally use hierarchical classification methods to describe their relationships.

A hierarchical structure is established in this classification tree , the highest level is the most general and general, each level is more specific than its previous level, and the lower level has the characteristics of the higher level, but also has subtle differences from the higher level. For example, after determining that a car is a passenger car, there is no need to indicate that it can be transported, because the passenger car itself is derived from the transport car class.

4.2 The concept of inheritance

1. The inheritance mechanism is the most important means for object-oriented programming to make code reusable. It allows programmers to expand and increase functions while maintaining the original characteristics.

2. A class B inherits from class A, or class B is derived from class A. In this case, class A becomes the base class (parent class), and class B becomes the derived class (subclass).

Members of derived classes, including two parts:

  • One class is inherited from the base class , and the other class is a member added by itself.

  • The ones inherited from the base class show their commonality, while the newly added members reflect their individuality.

4.3 Derived class definition

class 派生类名 : 继承方式 基类名
{
//派生类新增的数据成员和成员函数
//三种继承方式
public:     //公有继承
private:    //私有继承
protected:  //保护继承
};
#include <iostream>
#include <string> // 添加 <string> 头文件以使用 string 类
using namespace std;

class Person
{
public:
    void Print(){
        cout << "name: " << _name << endl;
        cout << "age: " << _age << endl;
    }
protected:
    string _name;   //姓名
    int _age;    //年龄
};

class Student: public Person
{
protected:
    int _stuid;
};

class Teacher:public Person
{
protected:
    int _jobbid;  //工号
};

int main()
{
    return 0;
}

The derived class/subclass inherits all member variables and functions in the base class/parent class, verify:

int main()
{
    Student s;
    Teacher t;
    cout << "sizeof(s): " << sizeof(s) << endl;
    cout << "sizeof(t): " << sizeof(t) << endl;
    s.Print();
    t.Print();
    return 0;
}

4.4 Derived class access control

1. The inheritance method in C++ will affect the external access attributes of subclasses

public inheritance: members of the parent class maintain the original access level in the subclass

Private inheritance: parent class members become private members in subclasses

Protected inheritance: the public members in the parent class will become protected, the protected members in the parent class will still be protected, and the private members in the parent class will still be private

2. Private members still exist in subclasses, but they cannot be accessed . No matter how the base class is inherited, the derived class cannot directly use the private members of the base class.

#include <iostream>
#include <string> // 添加 <string> 头文件以使用 string 类
using namespace std;

class A{
public:
    int x;
protected:
    int y;
private:
    int z;
public:
    void func_a()
    {cout << "func_a" << endl;}
};

class B: public A{
public:
    void handle_attrib()
    {this->y = 100;}
};

class C: private A
{
public:
    void handle_attrib()
    {
        this->x = 100;
        this->y = 100;
    }
};

class D: protected A
{
public:
    void handle_attrib()
    {
        this->x = 100;
        this->y = 100;
    }
};

int main()
{
    B b;
    b.x = 100;
    b.handle_attrib();
    cout << "sizeof(A): " << sizeof(A) << endl;
    cout << "sizeof(B): " << sizeof(B) << endl;

    C c;
    c.handle_attrib();

    D d;
    d.handle_attrib();
    return 0;
}

4.5 Calling order of constructor and destructor in single inheritance

  • When a subclass object is created, it will first call the constructor of the parent class

  • After the parent class constructor is executed , the subclass constructor is called

  • When the parent class constructor has parameters , it is necessary to call the parent class constructor explicitly in the subclass initialization list (parameter list)

  • Destructors are called in the reverse order of constructors

#include <iostream>
#include <string> // 添加 <string> 头文件以使用 string 类
using namespace std;

class A{
protected:
    int z;
private:
    int x;

public:
    A()
    {cout << "A()" << endl;}
    ~A()
    {cout << "~A()" << endl;}
    A(int a, int b):z(a), x(b)
    {cout << "A(int a, int b):z(a), x(b)" << endl;}
    
    int get_z(){return z;}
    int get_x(){return x;}
    void set_x(int x)
    {this->x = x;}
};

class B: public A {
public:
    B() { cout << "B()" << endl; }

    ~B() { cout << "~B()" << endl; }

    B(int a, int b) : A(a, b)
    {cout << "B(int a, int b)" << endl;}
};

int main()
{
    B t(10, 20);
    cout << t.get_z() << endl;
    cout << t.get_x() << endl;
    return 0;
}

4.6 The name of the member variable in the derived class and the member variable name in the base class conflict

#include <iostream>
using namespace std;

class A{
public:
    int x;
protected:
    int y;
private:
    int z;
public:
    A(){cout << "基类的构造函数" << endl;}
    ~A(){cout << "基类的构造函数" << endl;}
};

class E:public A
{
public:
    int x;
    E(){cout << "派生类的构思函数" << endl;}
    ~E(){cout << "派生类的析构函数" << endl;}
};

int main()
{
    E e;
    cout << "sizeof(E): " << sizeof(E) << endl;
    return 0;
}

From the execution results of the above code, we can draw conclusions:

The member variable in the derived class and the member variable with the same name in the base class coexist in the derived class.

So if you use a derived object to access the x variable, will you finally access x in the base class or in the derived class?

#include <iostream>
using namespace std;

class A{
public:
    int x;
protected:
    int y;
private:
    int z;
public:
    A(){this->x = 1; cout << "基类的构造函数" << endl;}
    A(int x):x(x) {cout << "基类的构造函数A(int x)" << endl;}
    ~A(){cout << "基类的析构函数" << endl;}
    void func_a(){cout << "func_a" << endl;}
};

class E:public A
{
public:
    int x;
    E(){cout << "派生类的构思函数" << endl;}
    E(int a):x(a){cout << "派生类的构造函数E(int a)" << endl;}
    ~E(){cout << "派生类的析构函数" << endl;}
};

int main()
{
    E e(10);
    cout << e.x << endl;
    cout << e.A::x << endl;
    cout << "sizeof(E): " << sizeof(E) << endl;
    return 0;
}

Summarize:

  • When a subclass member has the same name as a parent class member, the subclass still inherits the member with the same name from the parent class

  • If the subclass has a member with the same name as the parent class , the subclass accesses its members by default to access the members of the subclass (this scope, the principle of proximity )

  • Members with the same name are distinguished by scope:: in the subclass (use the same-named members of the base class in the derived class, and use the class name qualifier)

4.7 Hidden

1 The subclass function has the same name and the same parameters as the parent class function, but the parent class function has no virtual, and the parent class function is hidden

2 The function of the subclass has the same name as the parent class, but the parameters are different, and the parent class function is hidden

#include <iostream>
using namespace std;

class Father
{
public:
    void func1()
    {cout << "father func1" << endl;}
    void func2()
    {cout << "father func2" << endl;}
};

class Son: public Father
{
public:
    void func1()
    {cout << "son func1" << endl;}
    void func2()
    {cout << "son func2" << endl;}
};

int main()
{
    Son li;
    li.func1();
    li.func2();
    return 0;
}

4.8 Static Member Properties in Inheritance

1. Common features of static member functions and non-static member functions

  • They can all be inherited in derived classes

  • If you redefine a static member function , all other overloaded functions in the base class will be hidden

  • If we change the signature of a function in a base class, all base class versions using that function name will be hidden

2. Static member functions cannot be virtual functions (virtual function)

class Base{
public:
	static int getNum(){ return sNum; }
	static int getNum(int param){
		return sNum + param;
	}
public:
	static int sNum;
};
int Base::sNum = 10;

class Derived : public Base{
public:
	static int sNum; //基类静态成员属性将被隐藏
#if 0
	//重定义一个函数,基类中重载的函数被隐藏
	static int getNum(int param1, int param2){
		return sNum + param1 + param2;
	}
#else
	//改变基类函数的某个特征,返回值或者参数个数,将会隐藏基类重载的函数
	static void getNum(int param1, int param2){
		cout <<  sNum + param1 + param2 << endl;
	}
#endif
};
int Derived::sNum = 20;

4.9 Multiple Inheritance

1. The concept of multiple inheritance: We can inherit from one class, and we may also inherit from multiple classes at the same time , which is multiple inheritance.

2. The definition format of a derived class under multiple inheritance is as follows:

class <derived class name>:<inheritance method 1><base class name 1>,<inheritance method 2><base class name 2>,…

{

   <derived class body>

};

class A
{
…
};
class B
{
…
};
class C : public A, public B
{
…
};

4.10 Diamond Inheritance / Ring Inheritance

Multiple Inheritance (Multiple Inheritance ) refers to the ability to generate derived classes from multiple direct base classes, and multiple inherited derived classes inherit members of all parent classes. Although the concept is very simple, the interweaving of multiple base classes may cause intricate design problems, and naming conflicts are an unavoidable one.

Naming conflicts are easy to occur during multiple inheritance. Even if we carefully name the member variables and member functions in all classes with different names, naming conflicts may still occur, such as a typical diamond-shaped inheritance, as shown in the following figure:

Class A derives class B and class C, and class D inherits from class B and class C. At this time, the member variables and member functions in class A are inherited into class D and become two copies, one from the path of A-->B-->D, and the other from the path of A-->C-->D.

Reserving multiple members of the same name of the indirect base class in a derived class, although different data can be stored in different member variables, but in most cases this is redundant: because retaining multiple member variables not only takes up more storage space, but also easily produces naming conflicts. If class A has a member variable a, then direct access to a in class D will cause ambiguity, and the compiler does not know whether it comes from the path A --> B --> D or from the path A --> C --> D. The following is a specific implementation of diamond inheritance:

//间接基类A
class A{
protected:
    int m_a;
};
//直接基类B
class B: public A{
protected:
    int m_b;
};
//直接基类C
class C: public A{
protected:
    int m_c;
};
//派生类D
class D: public B, public C{
public:
    void seta(int a){ m_a = a; }  //命名冲突
    void setb(int b){ m_b = b; }  //正确
    void setc(int c){ m_c = c; }  //正确
    void setd(int d){ m_d = d; }  //正确
private:
    int m_d;
};
int main(){
    D d;
    return 0;
}

This code implements the diamond-shaped inheritance shown in the figure above. The code on line 25 tries to directly access the member variable m_a, but an error occurs because both class B and class C have member variable m_a (inherited from class A), and the compiler does not know which one to choose, so ambiguity arises.

To eliminate ambiguity, we can specify which class it comes from in front of m_a:

void seta(int a){ B::m_a = a; }

This indicates that m_a of class B is used. Of course, you can also use the C class:

void seta(int a){ C::m_a = a; }

6.6 Virtual Inheritance

In order to solve the problem of naming conflicts and redundant data in multiple inheritance , C++ proposes virtual inheritance, so that only one copy of the members of the indirect base class is kept in the derived class.

Adding the virtual keyword in front of the inheritance method is virtual inheritance, please see the following example:

//间接基类A
class A{
protected:
    int m_a;
};
//直接基类B
class B: virtual public A{  //虚继承
protected:
    int m_b;
};
//直接基类C
class C: virtual public A{  //虚继承
protected:
    int m_c;
};
//派生类D
class D: public B, public C{
public:
    void seta(int a){ m_a = a; }  //正确
    void setb(int b){ m_b = b; }  //正确
    void setc(int c){ m_c = c; }  //正确
    void setd(int d){ m_d = d; }  //正确
private:
    int m_d;
};
int main(){
    D d;
    return 0;
}

This code uses virtual inheritance to re-implement the diamond-shaped inheritance shown in the above figure , so that only one copy of the member variable m_a is reserved in the derived class D, and there is no ambiguity in direct access.

The purpose of virtual inheritance is to allow a class to make a declaration that it is willing to share its base class. Among them, this shared base class is called a virtual base class (Virtual Base Class), and A in this example is a virtual base class. Under this mechanism, no matter how many times the virtual base class appears in the inheritance system, only one member of the virtual base class is included in the derived class.

Guess you like

Origin blog.csdn.net/March_A/article/details/131880582