Detailed Explanation of C++ Inheritance - Base Class Derived Class Object Assignment Conversion, Diamond Virtual Inheritance

hello, this is bangbang, let's talk about inheritance today.

Object-oriented three major features: encapsulation, inheritance, polymorphism. 


Table of contents

1. The concept and definition of inheritance

  1.1 The concept of inheritance

  1.2 Inheritance definition

        1.2.1 Define format

        1.2.2 Inheritance and access qualifiers

        1.2.3 Changes in access methods of inherited base class members

2. Base class and derived class object assignment conversion

3. Scope in inheritance

​4. Default member functions of derived classes

5. Inheritance and Friends and Static Members

  5.1 Inheritance and Friends

  5.2 Inheritance and static members

6. Inheritance Model

6.1 Single inheritance

  6.2 Multiple inheritance

  6.3 Diamond inheritance

        6.3.1 Diamond virtual inheritance

        6.3.2 The principle of rhombus virtual inheritance

7. Inheritance summary


1. The concept and definition of inheritance

  1.1 The concept of inheritance

        Inheritance (inheritance) mechanism is the most important method for object-oriented programming to make code reusable . It allows programmers to expand and add functions while maintaining the characteristics of the original class, thus generating new classes, called derived classes. Inheritance presents object-oriented
The hierarchical structure of programming reflects the cognitive process from simple to complex. The reuse we have been exposed to before is function reuse .
Inheritance is the reuse of class design levels .
        
        To put it simply, inheritance means: put the same information in multiple classes into the same common class (parent class/base class), and reuse it in subclasses/derived classes.

  1.2 Inheritance definition

        1.2.1 Define format

Person is the parent class, also known as the base class. Student is a subclass, also known as a derived class.

        1.2.2 Inheritance and access qualifiers

        1.2.3 Changes in access methods of inherited base class members

class members/inheritance
public inheritance
protected inheritance private inheritance
public members of the base class
public members of derived classes
protected members of derived classes
private members of derived classes
protected members of the base class
protected members of derived classes
protected members of derived classes
private members of derived classes
private members of the base class
not visible in derived classes
not visible in derived classes
not visible in derived classes

Off-topic: We can see that the private members of the base class are invisible regardless of the inheritance method. In fact, this is the pitfall of the C++ bosses. The bosses want to include all situations in the design, but the actual In most cases, most projects use public and rarely private, but because of the upward development of the language, it is almost impossible for the bosses to roll back the language (except python3 is not compatible with python2) to avoid pitfalls, so they can only think of a way To fill this pit, although we know it is a pit, we still have to learn.

Summarize:

  1. Base class private members are not visible in derived classes no matter how they are inherited. Invisibility here  means that the private members of the base class are still inherited into the derived class object, but the derived class object is grammatically restricted from being able to access it no matter inside or outside the class  .
  2. The private members of the base class cannot be accessed in the derived class. If the base class members do not want to be directly accessed outside the class, but need to be accessible in the derived class, it is defined as protected. It can be seen that the protected member qualifier is due to inheritance .
  3. In fact, if we summarize the above table, we will find that the private members of the base class are not visible in the subclass. The access method of other members of the base class in the subclass == Min (the access qualifier of the member in the base class, inheritance method) , public > protected > private.
  4. When using the keyword class, the default inheritance method is private , and when using struct, the default inheritance method is public , but it is best to write the inheritance method explicitly .
  5. In practice, public inheritance is generally used, and protected/private inheritance is rarely used , and the use of protected/private inheritance is not advocated , because protected/private inherited members can only be used in derived classes Medium extensions are not very maintainable.

topic:

How to define a class that cannot be inherited?

answer:

C++98: Parent constructor private - subclasses are not visible. The subclass object is instantiated, and the constructor cannot be called.

C++11: Newly added keyword final (final class)


2. Base class and derived class object assignment conversion

  • Objects of derived classes can be assigned to objects of base classes/pointers of base classes/references of base classes . There is a figurative term here called slicing or cutting. It means to cut the parent class part of the derived class and assign it to the past.
  • Base class objects cannot be assigned to derived class objects.
  • The pointer or reference of the base class can be assigned to the pointer or reference of the derived class through cast. But it must be safe when the pointer of the base class points to the derived class object. Here, if the base class is a polymorphic type, you can use the dynamic_cast of RTTI (Run-Time Type Information) to identify and perform safe conversion.

1. Subclass objects can be directly assigned to parent class objects/pointers and references.

Student sobj ;
// 1.子类对象可以赋值给父类对象/指针/引用
Person pobj = sobj ;
Person* pp = &sobj;
Person& rp = sobj;

2. Base class objects cannot be assigned to derived class objects.

//2.基类对象不能赋值给派生类对象 
sobj = pobj;//错误

3. The pointer of the base class can be assigned to the pointer of the derived class through forced type conversion

 pp = &sobj
 Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
 ps1->_No = 10;
    
 pp = &pobj;
 Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
 ps2->_No = 10;

3. Scope in inheritance

1. In the inheritance system, the base class and the derived class have independent scopes .
2. There are members with the same name in the subclass and the parent class, and the subclass members will block the direct access of the parent class to the members with the same name . This situation is called hiding .
Also called redefinition. (In subclass member functions, you can use BaseClass::BaseClassMember Explicit Access )
3. It should be noted that if it is a member function hiding, only the same function name is required to constitute hiding .
4. Note that in practice, it is best not to define members with the same name in the inheritance system .

4. Default member functions of derived classes

  • Constructor

The subclass's own members are the same as the class. (The built-in type does not handle -> random value, give the default value, the default value handles; the custom type calls its processing)

To inherit members of the parent class, the constructor of the parent class must be called. If the base class does not have a default constructor, the call must be explicit during the initializer list phase of the subclass constructor.

First construct the parent class before constructing the subclass.

Subclasses are not allowed to directly initialize parent class member variables in the initialization list, and anonymous objects are initialized in the initialization list.

class Person
{
public:
    Person(const char* name)
	    : _name(name)
	{
		cout << "Person()" << endl;
	}
protected:
    string _name;
}
class Student:public Person
{
public:
    Student(const char* name, int num)
	    :Person(name)    //匿名对象在初始化列表初始
	    , _num(num)
    {
	    cout << "Student()" << endl;
    }
protected:
    int _num;
}
  • destructor

ditto

It is automatically called without explicitly calling the parent class destructor. The child class is destructed first, and then the parent class is destructed.

Because some subsequent scene destructors need to be rewritten, one of the conditions for rewriting is that the function name is the same . Then the compiler will perform special processing on the destructor name and process it as destructor() , so when the parent class destructor does not add virtual, the child class destructor and the parent class destructor form a hidden relationship.

  • copy constructor

Own members, like classes and objects. (The built-in type refers to copy -> shallow copy, and the custom type calls its copy construction)

Inherited parent class members must call the parent class copy constructor to initialize.

  • operator=

 ditto

 operator= must call the operator= of the parent class to complete the copy of the parent class.

5. Inheritance and Friends and Static Members

  5.1 Inheritance and Friends

Friend relationships cannot be inherited , that is to say, parent class friends cannot access subclass private and protected members

  5.2 Inheritance and static members

If the base class defines a static static member, there is only one such member in the entire inheritance system . No matter how many children are derived
Classes have only one static member instance.

6. Inheritance Model

6.1 Single inheritance

Single inheritance: When a subclass has only one direct parent class, the inheritance relationship is called single inheritance

  6.2 Multiple inheritance

Multiple inheritance: When a subclass has two or more direct parent classes, the inheritance relationship is called multiple inheritance


topic:

 Multiple inheritance: (object distribution) the first inheritance is in the front, and the later inheritance is in the back.

slice:

 You can see that p1 and p3 actually point to the same place.

The member variable is a local variable placed on the stack, and the input element is placed on the top of the stack, and the address gradually increases. b2 is inherited later, so the address is higher than b1, that is, p2 is higher than p1.


  6.3 Diamond inheritance

Diamond inheritance is a special case of multiple inheritance
The problem of diamond inheritance: From the following object member model construction, it can be seen that diamond inheritance has data redundancy (repetitively contains _name) and ambiguity (it is impossible to know which _name is accessed).

        6.3.1 Diamond virtual inheritance

Virtual inheritance (virtual) can solve the problem of ambiguity and data redundancy of diamond inheritance.
As in the inheritance relationship above, the problem can be solved by using virtual inheritance when the Student and Teacher inherit Person.
Diamond virtual inheritance object model:

        6.3.2 The principle of rhombus virtual inheritance

A simplified diamond inheritance model is given here for research.

class A
{
public:
	int _a;
};

//class B : public A
class B : virtual public A
{
public:
	int _b;
};

//class C : public A
class C : virtual public A
{
public:
	int _c;
};

class D : public B, public C
{
public:
	int _d;
};

int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._a = 0;
	d._b = 3;
	d._c = 4;
	d._d = 5;

	return 0;
}
  • First do not use virtual inheritance virtual, the memory situation is as follows:

 The storage situation conforms to the diamond-shaped inheritance object model we gave above.

  •  Using virtual inheritance virtual, the memory situation is as follows:

We can see that in the D object, A is placed at the bottom of the object composition. This A belongs to B and C (common A) at the same time. There are 2 addresses where B and C store A. These 2 addresses (that is, The virtual base table pointer) points to a table (virtual base table), and the offset is stored in the virtual base table, and the common A is found through the offset.

7. Inheritance summary

        1. The syntax of C++ is complex, and multiple inheritance is a manifestation. With multiple inheritance, there is diamond-shaped inheritance, and with diamond-shaped inheritance, there is diamond-shaped virtual inheritance, and the underlying implementation is very complicated. Therefore, it is generally not recommended to design multiple inheritance, and you must not design diamond inheritance. Otherwise, there are problems in complexity and performance.
        2. Multiple inheritance can be considered as one of the defects of C++, and many later languages ​​do not have multiple inheritance.

        3. Inheritance and composition 

  • Public inheritance is an is-a relationship. That is to say, every derived class object is a base class object. Such as: people - students.
  • Composition is a has-a relationship. Assuming B composes A, there is an A object inside each B object. Such as: tires - cars.
  • Prefer object composition over class inheritance.
  • Inheritance allows you to define derived class implementations in terms of base class implementations. This kind of reuse by generating derived classes is often called white-box reuse. The term "white box" is relative to visibility: in inheritance, the internal details of the base class are visible to subclasses. Inheritance destroys the encapsulation of the base class to a certain extent , and the change of the base class has a great impact on the derived class. The dependency relationship between the derived class and the base class is very strong, and the coupling degree is high .
  • Object composition is an alternative to class inheritance for reuse. New and more complex functions can be obtained by assembling or combining objects. Object composition requires that the objects being composed have well-defined interfaces. This style of reuse is called black-box reuse because the internal details of the objects are invisible. Objects only appear as "black boxes". There is no strong dependency relationship between composite classes, and the degree of coupling is low . Preferring object composition helps you keep each class encapsulated.
  • In practice, use as many combinations as possible. The coupling degree of the combination is low, and the code maintainability is good. However, inheritance is also useful. Some relationships are suitable for inheritance, so use inheritance. In addition, to achieve polymorphism, inheritance is also necessary . The relationship between classes can use inheritance, you can use combination, use combination.

Guess you like

Origin blog.csdn.net/bang___bang_/article/details/130677253