c++: Inheritance (super detailed explanation)

Table of contents

One: What is inheritance

Two: Inherited format

Summary of Inheritance:

Two: subclass and parent class (base class and derived class)

1. Mutual assignment of subclasses and parent classes:

2. Member variables with the same name

3. Member function with the same name

Three: Default member functions in subclasses

1. Constructor

2. Destructor

3. Copy construction

4. Assignment operator overloading

 Four: single inheritance and multiple inheritance

Single inheritance:

 Multiple inheritance:

diamond inheritance

Solution one:

Solution two:

Summary of single inheritance and multiple inheritance:


One: What is inheritance

definition:

Inheritance (inheritance) mechanism is the most important means to make code reusable in object-oriented programming, which allows programmers to extend and add functions on the basis of maintaining the original class characteristics. The new class generated in this way is called a derived class (or subclass), and the inherited class is called a base class (or parent class).

Inheritance presents the hierarchical structure of object-oriented programming, reflecting the cognitive process from simple to complex. The reuse that I have been in contact with before is function reuse, and inheritance is the reuse of class design level.

Well, I can’t see anything just by looking at it, so let’s go directly to the code

code:

class human {//定义了一个父类,名字叫human
public:
	string name = "小明";//父类里面定义了一个string类型的和一个int类型
	int age = 18;
};
class student:public human {//定义了一个以public方式继承父类的子类student
public:
	int schoolnum = 666;//在父类的name和age的基础上增加了一个schoolnum
	void print()
	{
		cout << name << endl << age << endl << schoolnum << endl;//输出
	}
};
int main()
{
	student st;
	st.print();
	return 0;
}

The result is as follows:

 Okay, I still don’t understand, so let’s split this code in half

first part:

class human {
public:
	string name = "小明";
	int age = 18;
};

the second part:

class student:public human {
public:
	int schoolnum = 666;
	void print()
	{
		cout << name << endl << age << endl << schoolnum << endl;
	}
};

In this way, we found that the first part of the code is actually the class we usually use.

For the interpretation of the second part, let's give an example

If we want to design a school system, then for students, teachers... a series of people, we need to enter the necessary information such as name and age, but only for a certain type of people, such as students, except for the necessary In addition to the information, there is also a separate student number. For example, in addition to the necessary information, the teacher also has a separate employee number.

 So in order to be lazy and improve efficiency, we can encapsulate the name and age into a class class, which is our first part of the code, and then create a new class, inherit the original name and age class, and then add it Student number/employee number, which is our second category.

So how to inherit it?

Two: Inherited format

class 新类的名字:继承方式 继承类的名字{};

Take my example just now as an example

class student:public human{};
//student是新类的名字,public是继承方式,human是要继承的类
//意思就是说我定义了一个名叫 student的类 以public的方式 来继承你human

We have two names for student and human here.

One is the base class (human) and derived class (student) in the textbook.

I personally like the second type of parent (human) and child (student). After all, it feels like inheriting a family property.

Three: Inherited subclass member access rights

Here I will throw a picture first, which is what the teacher insists on reciting in the textbook

insert image description here

 Here I share a very ingenious way

Let us suppose

public>protectd>private

We take the smaller of x and y.

 The last private is not allowed. In this way, we can easily remember

Summary of Inheritance:

1. The private members of the base class are not visible to the derived class 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 member does not want to be accessed directly outside the derived class, but needs to be accessed in the derived class, it is defined as protected. It can be seen that the protected member qualifier is due to inheritance.
3. 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 is the one with less permission in the access qualifier and inheritance method (permission sorting: 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.

Two: subclass and parent class (base class and derived class)

1. Mutual assignment of subclasses and parent classes:

code:

class human {//父类
public:
	string name = "小明";
	int age = 18;
};
class student:public human {//子类
public:
	int schoolnum = 666;
};
int main()
{
	student st;
	human hm;
	hm = st;//将子类赋值给父类
	st = hm;//将父类赋值给子类
	return 0;
}

There will be such a result:

 Here we introduce something called the slicing principle

 Because there is no schoolnum in the parent class, after the parent class receives the name and age passed by the subclass, the redundant schoolnum is ignored. But if the parent class is passed to the child class, one less is passed, so an error will be reported.

At the same time, we give three assignment methods

Assignment in three ways:

One: = sign

student st;//子类
	human hm;//父类
	hm = st;

 

2: Quote

student st;//子类
	human& hm=st;父类

Three: pointer

student st;//子类
	human* hm=&st;//父类

2. Member variables with the same name

In some cases, the same member variable appears in the parent class and the subclass, as follows name

class human {
public:
	string name = "小明";
};
class student:public human {
public:
	string name = "小红";
	void print()
	{
		cout << name << endl;
	}
};
int main()
{
	student st;
	st.print();
	return 0;
}

At this time, the compiler gives priority to subclasses

The result is as follows

But if we just want to access the member variable of the parent class, we need to add modification

void print()
	{
		cout << human::name << endl;
	}

 In fact, it is also easy to understand, the default subclass, the parent class is modified and limited

3. Member function with the same name

As follows, the same function print exists in both the parent class and the subclass

class human {
public:
	string name = "小明";
	void print()
	{
		cout << name << endl;
	}
};
class student :public human {
public:
	string name = "小红";
	void print()
	{
		cout << name << endl;
	}
};
int main()
{
	student st;
	st.print();
	return 0;
}

This constitutes hiding. (Function overloading is in the same scope, where the parent class and subclass are two scopes)

The function is hidden, the compiler will call the matching function in the subclass by default, if there is no compiler, an error will be reported

The result of the above is as follows

Although the hiding of member functions only requires the same function name to constitute hiding, there is no requirement for the parameter list.

 But let's modify the function of the subclass

void print(int x)//我们对子类的print函数加入一个参数
	{
		cout << name << endl;
	}

 This is because the compiler calls the print function in the subclass by default, but the only print function in the subclass has a default parameter, so the compiler cannot find a matching print function, so it will report an error.

Three: Default member functions in subclasses

1. Constructor

By default, the compiler will call the constructor of the parent class first, and then call the constructor of the subclass, as follows

class human {
public:
	human(string name = "小明")//先调用:父类默认析构调用一个print打印name
		:_name(name)
	{
		cout << name << endl;
	}
protected:
	string _name;
};


class student :public human {//后调用:子类默认析构调用一个print打印name和age
public:
	student(string name,int age)
		:_age(age)
	{
		cout << name << endl<<age<<endl;
	}
protected:
	int _age;
};


int main()
{
	student st("小红", 18);
	return 0;
}

 The result is as follows

 It can be seen that the compiler first calls the destructor of the parent class, prints out Xiaoming, and then calls the destructor of the subclass again to print out Xiaohong and age.

So please be sure to ensure that the parent class destructor is valid, if the parent class destructor fails

human(string name)//你这里不传值,那么就不能完成初始化,相当于父类析构失效
		:_name(name)
	{
		cout << name << endl;
	}

Then you must assign a value to the parent class destructor in the subclass

student(string name,int age)
		:_age(age)
		, human(name)//新增,子类以自己的name给父类的析构中的name赋值,age和name的顺序随意变动

The result is as follows

 If it doesn’t work, delete the destructor of the parent class. Anyway, the compiler will generate it by default.

2. Destructor

The destructor is the opposite of the constructor. By default, the compiler calls the destructor of the subclass first, and then calls the destructor of the parent class.

Verify as follows:

We add two destructors to the original code

class human {
public:
	human(string name = "小明")
		:_name(name)
	{}
	~human()
	{
		cout << "我是父类" << endl;
	}
protected:
	string _name;
};
class student :public human {
public:
	student(string name,int a = 20)
		:age(a)
	{}
	~student()
		
	{
		cout <<"我是子类"<< endl;
	}
protected:
	int age;
};
int main()
{
	student st("小明", 18);
	return 0;
}

The result is as follows:

 so

Never call the destructor of the parent class in the child class

Never call the destructor of the parent class in the child class

Never call the destructor of the parent class in the child class

If it is a pointer type, then the same area is destructed twice, which will cause the problem of wild pointers.

3. Copy construction

When calling the copy construction of the parent class in the subclass, just pass in the subclass object directly, and the copy construction of the parent class will get the part of the parent class through "slicing".

class human {
public:
	human(string name="小明")
		:_name(name)
	{
		cout << name << endl;
	}
protected:
	string _name;
};
class student:public human {
public:
	student(string name, int age)
		:_age(age)
	{
		cout << name << endl << age << endl;
	}
	student(student& s)
		:human(s)//直接将st传过来通过切片拿到父类中的值
		,_age(s._age)//拿除了父类之外的值
	{
		cout << s._age << endl<<s._name<<endl;
	}
protected:
	int _age;
};
int main()
{
	student st("小红",18);
	student st2(st);
	return 0;
}

The result is as follows:

4. Assignment operator overloading

The operator= of the subclass must explicitly call the operator= of the parent class to complete the assignment of the parent class.

Because the operator of the subclass and the parent class are given the same name by default by the compiler, it is hidden, so every time the assignment operator = is called, the subclass will always be called, which will cause a cycle, so the assignment here must be direct Modifier Qualified Parent

class human {
public:
	human(string name = "小明")
		:_name(name)
	{
	}
	human& operator=(const human& p)
	{
		if (this != &p)
		{
			cout << "调用父类" << endl;
			_name = p._name;
		}
		return *this;
	}
protected:
	string _name;
};
class student :public human {
public:
	student(string name, int age)
		:_age(age)
	{
	}
	student(student& s)
		:human(s)
		, _age(s._age)
	{
	}
	student& operator=(const student& s)
	{
		if (this != &s)
		{
			cout << "调用了子类" << endl;
			human::operator=(s);//必须调用父类运算符
			_age = s._age;
			_name = s._name;
		}
		return *this;
	}
protected:
	int _age;
};
int main()
{
	student st("小红", 18);
	student st2(st);
	student st3("小刚", 16);
	st = st3;
	return 0;
}

 The result is as follows:

 

 Four: single inheritance and multiple inheritance

Single inheritance:

A subclass has only one direct parent class inheritance relationship.

 Multiple inheritance:

A subclass has two or more direct parent class inheritance relationships.

 From the above two points, we will find a very painful inheritance ,

diamond inheritance

Ok, let's start with a classic diamond inheritance code 

This is a code that is problematic

class A {
public:
	string name;
};
class B :public A {
public:
	int age;
};
class C :public A {
public:
	string sex;
};
class D :public B, public C {
public:
	int id;
};
int main()
{
	D student;
	student.name = "小明";
	student.age = 18;
	student.sex = "男";
	student.id = 666;
	return 0;
}

With a snap, soon, an error message will come out. 

 Because the name here exists in both B and C, so D does not know whether to inherit the name of B or the name of C

This is what leads to the problem of code redundancy and ambiguity.

So we have two solutions

Solution one:

Modification limit

student.B::name = "小明";

Here we specify to inherit the name in B, so there will be no conflict

Solution two:

Virtual inheritance: add virtual before the inheritance method.

class B :virtual  public A {
public:
	int age;
};
class C :virtual public A {
public:
	string sex;
};

Summary of single inheritance and multiple inheritance:

Don't use diamond inheritance

Multiple inheritance is a manifestation of the complexity of C++. With multiple inheritance, there is diamond inheritance. In order to solve diamond inheritance, diamond virtual inheritance has appeared, and its underlying implementation is very complicated. Therefore, it is generally not recommended to design multiple inheritance, and you must not design diamond inheritance.

Guess you like

Origin blog.csdn.net/qq_62718027/article/details/125922249