c + + inheritance (basic)

catalogue

1. Basic concept and definition of inheritance

1.1 basic concept of inheritance

1.2 definition of succession

2. Assignment conversion of base class and derived class objects

3. Scope in inheritance

 4. Default member function of derived class

4.1 calling order of constructor destructor of base class derived class

5. Inheritance and friendship

6. Static member variables in inheritance

7. Complex diamond inheritance and diamond virtual inheritance

 8. Summary and reflection of inheritance

1. Basic concept and definition of inheritance

1.1 basic concept of inheritance

Inheritance mechanism is the most important means for object-oriented programming to make code reusable. It allows programmers to maintain the original class characteristics
Expand and add functions on the basis of sex, so as to produce a new class, called derived class. Inheritance presents the hierarchy of object-oriented programming,
It reflects the cognitive process from simple to complex. The reuse we used to contact was function reuse, and inheritance was class design level reuse.

#include<iostream>
using namespace std;
class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "peter"; // full name
	int _age = 18; // Age
};
// After inheritance, the members of the Person of the parent class (member function + member variable) will become part of the child class. Student and
//Teacher reuses the members of Person. Next, we use the monitoring window to view the Student and teacher objects, and we can see the reuse of variables.
//Call Print to see the reuse of member functions.
class Student : public Person
{
protected:
	
		int _stuid; // Student number
};
class Teacher : public Person
{
protected:
	int _jobid; // Job number
};
int main()
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	return 0;
}

1.2 definition of succession

#include<iostream>
#include<string>
using namespace std;
class Person {
public:
	Person(const string& name="Peter", int age=18)
		:_name(name)
		,_age(age)
	{}
	

	string _name;
	int _age;
};
     Subclass inheritance method parent class
class Men :public Person {
public:
	int id;
};
int main() {
	return 0;
}

Here, Men is called a derived class, also known as a subclass, and Person is called a parent class, also known as a base class. In class Men:public Person, public represents the inheritance method.

1.2.2 several ways of inheritance:

1.2.3 changes in access methods of inherited base class members:

Summary:

1. The private member of the base class is invisible in any way of inheritance in the derived class. Invisible here refers to the private member of the base class or
It is inherited into the derived class object, but the syntax restricts the derived class object from accessing it both inside and outside the class.
2. The private member of the base class cannot be accessed in the derived class. If the member of the base class does not want to be accessed directly outside the class, it needs to be accessed in the derived class
Access is defined as protected. It can be seen that the protected member qualifier appears due to inheritance.
3. In fact, when we summarize the above table, we will find that the private members of the base class are invisible in subclasses. Other members of the base class are in subclasses
Access method = = min (access qualifier and inheritance method of members in the base class), public > protected > private.
4. When using the keyword class, the default inheritance method is private. When using struct, the default inheritance method is public, but it is best displayed
Write the inheritance method.
5. In practical application, public inheritance is generally used, and protected / private inheritance is rarely used, nor is it advocated
Protected / private inheritance, because the members inherited from protected / private can only be used in the classes of derived classes. In practice
Poor expansibility and maintainability.

2. Assignment conversion of base class and derived class objects

1. The derived class object can be assigned to the object of the base class / the pointer of the base class / the reference of the base class. There is an image called slicing or cutting. The implication is to cut the part of the parent class in the derived class to assign values in the past.
2. The base class object cannot be assigned to the derived class object
3. The type of pointer that can be cast to the derived base class. However, it is safe only when the pointer to the base class points to the derived class object. Here, if the base class is a polymorphic type, you can use the dynamic of RTTI (run time type information)_ Cast for identification and security conversion.

Let's take an example:

#include<iostream>
#include<string>
using namespace std;
class Person {
public:
	Person(const string& name="Peter", int age=18,string sex="male")
		:_name(name)
		,_age(age)
        ,sex(sex)
	{}
	
protected:
	string _name;
	int _age;
   string sex;

};
class Men :public Person {
public:
	int id;
};
int main() {
	Men M;
	return 0;
}

Corresponding code verification:

#include<iostream>
#include<string>
using namespace std;
class Person {
public:
	Person(const string& name="Peter", int age=18,string sex="male")
		:_name(name)
		,_age(age)
		,sex(sex)
	{}
	
protected:
	string _name;
	int _age;
	string sex;
};
class Men :public Person {
public:
	int id;
};
int main() {
	Men M;
	//A subclass object can be a pointer to the parent class
	Person* p = &M;
	//The subclass object can be assigned to the reference of the parent class
	Person& t = M;
	//Subclass objects can be assigned to parent objects, not vice versa
	Person tmp = M;
	// 3. The pointer of the base class can be assigned to the pointer of the derived class through forced type conversion
	Person* parent = new Person("Li Si",25, "male");
	Men* sub = (Men*)parent;
	return 0;
}

 3. Scope in inheritance

1. In the inheritance system, both base and derived classes have independent scopes.
2. There are members with the same name in the subclass and parent class. The subclass members will block the direct access of the parent class to the members with the same name. This situation is called hiding or resetting
Righteousness. (in subclass member functions, you can use base class:: base class member display access)
3. If the function name is hidden, only the function name needs to be hidden.
4. Note that in practice, it is better not to define members with the same name in the inheritance system.

5. It should be noted that if a function with the same name appears in the parent class and child class, this is called hiding or redefinition. It is not a function overload. The premise of function overload is to be in the same scope.

Example 1:

#include<iostream>
#include<string>
using namespace std;
// Student's_ num and Person_ num constitutes a hidden relationship. It can be seen that although such code can run, it is very easy to be confused
class Person
{
protected:
	string _name = "Little plum"; // full name
	int _num = 111; // ID number
};
class Student : public Person
{
public:
	void Print()
	{//If the scope is not specified, the variables or functions with the same name in the subclass will be accessed first
		cout << " full name:" << _name << endl;
		cout << " ID number:" << Person::_num << endl;
		cout << " Student number:" << _num << endl;
	}
protected:
	int _num = 999; // Student number
};
void Test()
{
	Student s1;
	s1.Print();
};
int main() {
	Test();
	return 0;
}

Example 2:

#include<iostream>
#include<string>
using namespace std;
// Fun in B and fun in A do not constitute overloads because they are not in the same scope
// Fun in B and fun in A constitute hiding. If the member function satisfies the same function name, it constitutes hiding.
class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B : public A
{
public:
	void fun(int i)
	{
		A::fun();
		cout << "func(int i)->" << i << endl;
	}
};
void Test()
{
	B b;
	b.fun(10);
};
int main() {
	Test();
	return 0;
}

If we want the function with the same name in the parent class to be visible in the child class. In c++11, the function with the same name in the parent class is visible in the subclass. Through the using keyword, it means that the function with the same name in the parent class is used in the subclass in the way of overload.

Example:

#include<iostream>
#include<string>
using namespace std;
// Fun in B and fun in A do not constitute overloads because they are not in the same scope
// Fun in B and fun in A constitute hiding. If the member function satisfies the same function name, it constitutes hiding.
class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B : public A
{
public:
	//In c++11, the function with the same name in the parent class is visible in the subclass through the using keyword,
	// To put it bluntly, let the function with the same name in the parent class be used in the way of overloading in the child class
	using A::fun;
	void fun(int i)
	{
		
		cout << "func(int i)->" << i << endl;
	}
};
void Test()
{
	B b;
	b.fun();
};
int main() {
	Test();
	return 0;
}

Summary:

Characteristics of overloaded member functions:

1. Within the same scope.

2. The function name is the same.

3. Different parameters.

4.virtual is optional

Override means that a derived class function overrides a function of the same name in the base class

1. Different scopes

2. Same function name

3. Same parameters

4. The base class function must have the virtual keyword

Hiding means that the function of the derived class shields the function with the same name in the base class. The rules are as follows

1. If the function name of the derived class is the same as that of the base class, but the number of parameters is different, the base class function will be hidden whether there is virtual or not

2. If the function name of the derived class is the same as that in the base class, but the number of parameters is the same, but the base class function has no virtual, the base class function will also be hidden.

 4. Default member function of derived class

6 default member functions, "default" means that if we don't write, the compiler will change and we will automatically generate one. In the derived class, these
How are member functions generated?
1. The constructor of the derived class must call the constructor of the base class to initialize that part of the members of the base class. If the base class does not have a default constructor
Number, the call must be displayed during the initialization list phase of the derived class constructor.
2. The copy constructor of the derived class must call the copy constructor of the base class to complete the copy initialization of the base class.
3. The operator = of the derived class must call the operator = of the base class to complete the replication of the base class.
4. The destructor of the derived class will automatically call the destructor of the base class to clean up the members of the base class after being called. Because this can guarantee the derived class
Object to clean up derived class members before cleaning up base class members.
5. When initializing a derived class object, first call the base class construction, and then call the derived class construction.
6. Derived class object destruct cleanup calls derived class destruct first, and then calls the destruct of base class.

4.1 calling order of constructor destructor of base class derived class

In inheritance, if the subclass inherits the parent class, when defining the subclass object:

1. Call the constructor of the parent class before calling the constructor of the child class.

2. When destructing, it is the opposite. First call the constructor of the subclass and then call the destructor of the parent class

3. Copy constructor is similar to operator = constructor

4. if the constructor is written in the base class and the constructor is called in the base class, the call constructor can be displayed in the list of initialization parameters in the subclass.

Example:

#include<iostream>
#include<string>
using namespace std;

class Person{
	public:
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	Person(const Person&p)
		:_name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
		
	}
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // full name
};

class Student : public Person
{
public:
	Student(const char* name, int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}
	Student(const Student& s)
		: Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}
	Student& operator = (const Student& s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //Student number
};
void Test()
{
	Student s1("jack", 18);
	Student s2(s1);
	Student s3("rose", 17);
	s1 = s3;
}
int main() {
	Test();
	return 0;
}

Interview question: how to define a class that cannot be inherited:

A: set the constructor or destructor of this class to private.  

5. Inheritance and friendship

Friend relationships cannot be inherited, that is, friends of a base class cannot access private and protected members of subclasses

Example:

class Student;
class Person
{
public:
friend void Display(const Person& p, const Student& s);
protected:
string _name; // full name
};
class Student : public Person
{
protected:
int _stuNum; // Student number
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl;
cout << s._stuNum << endl;
}
int main()
{
Person p;
Student s;
Display(p, s);
}

In the above example, the Display function is a friend function of the base class, but it is not a friend function of the subclass. Just like our father has his own friends, but his friends are not necessarily our friends. Therefore, the friend function of the parent class cannot access the friend function of the child class.

6. Static member variables in inheritance

If the base class defines static members, there is only one such member in the whole inheritance system. No matter how many subclasses are derived, there is only one
static member instances.

Example:

class Person
{
public :
Person () {++ _count ;}
protected :
string _name ; // full name
public :
static int _count; // Count the number of people.
};
int Person :: _count = 0;
class Student : public Person
{
protected :
int _stuNum ; // Student number
};
class Graduate : public Student
{
protected :
string _seminarCourse ; // Research subjects
};
void TestPerson()
{
Student s1 ;
Student s2 ;
Student s3 ;
Graduate s4 ;
cout <<" Number of people :"<< Person ::_count << endl;
Student ::_count = 0;
cout <<" Number of people :"<< Person ::_count << endl;
}

Operation results:

 7. Complex diamond inheritance and diamond virtual inheritance

Let's start with the following:

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

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

Diamond inheritance: Diamond inheritance is a special case of multi inheritance

Diamond inheritance problem: from the above object member model construction, we can see that diamond inheritance has the problems of data redundancy and ambiguity

Let's take an example:

#include<iostream>
#include<string>
using namespace std;

class A {
public:
	int m_age;
};
class B :public A {
};
class C :public A {

};
class D :public B, public C {

};
void Test() {
	D d;
}
int main() {
	
	return 0;
}

In the above code, D inherits two copies of m_age comes from his base classes B and C, respectively.

At this point, if we use d to access m_age will be ambiguous, as shown in the figure:

#include<iostream>
#include<string>
using namespace std;

class A {
public:
	int m_age;
};
class B :public A {
};
class C :public A {

};
class D :public B, public C {

};
void Test() {
	D d;
	cout << d.m_age << endl;
}
int main() {
	
	return 0;
}

If we want to access at this time, we need to specify the scope and m_age is the of that base class. How to prove it? Here's a way for bloggers to introduce:

First, we click item properties:

Then find the command line in c/c + +

Command line found:

Enter in other options

/d1 # reportAllClassLayout # is to view the layout of all classes

/d1 # reportSingleClassLayoutXX # where "XX" is the name of the class you want to view

3. After configuration, save. Then recompile the project, and you can see the following figure in [output]

We can see that the DD class inherits two copies of m_age, which is completely repeated, and access M_ You also need to specify the scope before you can access.

In order to solve this problem, virtual inheritance is introduced into c + + to solve the ambiguity of diamond inheritance and data redundancy.

 

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:
	string _name; // full name
};
class Student : virtual public Person
{
protected:
	int _num; //Student number
};
class Teacher : virtual public Person
{
protected:
	int _id; // Employee number
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // Major courses
};
void Test()
{
	Assistant a;
	a._name = "peter";
}
int main() {
	Test();
	return 0;
}

Note: virtual inheritance should not be used elsewhere.

Let's explore the principle of virtual inheritance in diamond inheritance: in order to study the principle of virtual inheritance, we give a simplified diamond inheritance system.

#include<iostream>
#include<string>
using namespace std;

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 DD : public B, public C
{
public:
	int _d;
};
int main()
{
	DD d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

Similarly, we view the object model in the output list according to the above operation

In the following figure, the virtual object A can inherit from the virtual object D
Both belong to B and C, so how do B and C find the public A? Here is A table pointed to by the two pointers of B and C. These two fingers
Needles are called virtual base table pointers, and these two tables are called virtual base tables. The offset stored in the virtual base table. The following A can be found by the offset.

  

 

 8. Summary and reflection of inheritance

1. Many people say that C + + syntax is complex. In fact, multi inheritance is an embodiment. With multiple inheritance, there is diamond inheritance, and with diamond inheritance, there is
Diamond virtual inheritance, the underlying implementation is very complex. Therefore, it is generally not recommended to design multi inheritance, and diamond inheritance must not be designed. Otherwise in
There are problems in complexity and performance.
2. Multi inheritance can be considered as one of the defects of C + +. Many subsequent OO languages do not have multi inheritance, such as Java.

3. Inheritance and combination

1.public inheritance is an is-a relationship. That is, each derived class object is a base class object.
Combination is A has-a relationship. Suppose B combines A, and there is an A object in each B object.
Object composition is preferred over class inheritance.
Inheritance allows you to define the implementation of a derived class based on the implementation of the base class. This reuse by generating derived classes is usually called white box reuse
(white-box reuse). The term "white box" is relative 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. The change of the base class has a great impact on the derived class. Dependency relationship between derived class and base class
The system is very strong and has high coupling.
2. Object composition is another reuse option besides class inheritance. New and more complex functions can be obtained by assembling or combining objects. yes
Image composition requires that the combined objects have well-defined interfaces. This reuse style is called black box reuse,
Because the internal details of the object are invisible. Objects only appear as "black boxes". There is no strong dependency between composite classes,
Low coupling. Prioritizing object composition helps you keep each class encapsulated.
Actually, try to use as many combinations as possible. The combination has low coupling and good code maintainability. However, inheritance can also be useful, and some relationships are suitable
If you want to achieve polymorphism, you must also inherit. The relationship between classes can be inherited or combined
Use combination.

#include<iostream>
#include<string>
using namespace std;
// Car and BMW Car and Benz constitute the relationship of is-a
class Car {
protected:
	string _colour = "white"; // colour
	string _num = "Shaanxi ABIT00"; // license plate number
};
class BMW : public Car {
public:
	void Drive() { cout << "Good drive-Manipulation" << endl; }
};
class Benz : public Car {
public:
	void Drive() { cout << "Okay, sit down-comfortable" << endl; }
};
// The relationship between Tire and Car constitutes has-a
class Tire {
protected:
	string _brand = "Michelin"; // brand
	size_t _size = 17; // size
};
class Car {
protected:
	string _colour = "white"; // colour
	string _num = "Shaanxi ABIT00"; // license plate number
	Tire _t; // tyre
};
int main()
{
	
	return 0;
}

Finally: if you think it is helpful to you, please move your little hand and point a like below, or if you find an error, please leave a message in the comment area.

Keywords: C++ Back-end

Added by oliverw92 on Wed, 02 Mar 2022 15:30:37 +0200