Table of contents
2. The relationship between access qualifiers and inheritance methods
3. Changes in access methods inherited from parent class members
3. Assignment and transformation of parent class and subclass objects
5. Different default member functions for derived classes
3. Assignment symbol overloading
7. Inheritance and static members
2. Thinking: How to make a class that cannot be inherited
8. Diamond inheritance and virtual diamond inheritance
1. The problem of diamond inheritance
2. Virtual inheritance solution
3. Low-level details of virtual inheritance
9. Inheritance and combination
1. Concept
The one based on the original class is generally called the base class (base_class). I think it is easier to understand that it is called the parent class .
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "peter"; // 姓名
int _age = 18;
};
class Student : public Person
{
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
int main()
{
Student s;
Teacher t;
s.Print();
t.Print();
t.Print();
return 0;
}
After inheritance, the members (member functions + member variables) of the parent class's Person will become part of the subclass. This reflects that Student and Teacher reuse members of Person. Next, we use the monitoring window to view the Student and Teacher objects, and we can see the reuse of variables. You can see the reuse of member functions by calling Print .
![](https://img-blog.csdnimg.cn/42cec2c5efc8432abdf8e3d96090264d.png)
2. Inheritance definition
1. Inheritance format
Below we see that Person is the parent class, also called the base class. Student is a subclass, also called a derived class
2. The relationship between access qualifiers and inheritance methods
The default inheritance method when using the keyword class is private , and the default inheritance method when using struct is public , but it is best to write the inheritance method explicitly.
3. Inherit changes in access methods of parent class members
summary:
1. The private members of the parent class are invisible in the derived class no matter how they are inherited. Invisible here means that the private members of the parent class are still inherited into the derived class object, but the syntax restricts the derived class object from accessing it whether inside or outside the class .
2. Although the private members of the parent class are inherited in the derived class, the private members of the parent class are not visible (due to permission issues). If the members of the parent class do not want to be directly accessed outside the class, but they need to be accessible in the derived class, It is defined as protected. It can be seen that the protected member qualifier appears because of inheritance .
3. Subclass access permission = the minimum permission of the modifier and inheritance mode. For example: the modifier is private, the inheritance mode is public, and the final permission is private. Permissions from large to small are: public > protected > private
4. In practical applications, public inheritance is generally used, and protected/private inheritance is rarely used. The use of protected/private inheritance is not recommended, because members inherited from protected/private can only be used in derived classes. In practice, expansion and maintainability are not strong. So basically, the common way to deal with inheritance: public . Handle member variables, functions: use public / protected
3. Assignment and transformation of parent class and subclass objects
class Person
{
protected:
string _name; // 姓名
string _sex;
int _age; // 年龄
};
class Student : public Person
{
public:
int _No; // 学号
};
void Test()
{
Student sobj;
// 1.子类对象可以赋值给父类对象/指针/引用
Person pobj = sobj;
Person* pp = &sobj;
Person& rp = sobj;
//2.父类对象不能赋值给子类对象
// sobj = pobj;
// 3.父类的指针可以通过强制类型转换赋值给子类的指针
pp = &sobj;
Student * ps1 = (Student*)pp; // 这种情况转换时可以的。
ps1->_No = 10;
// 父类利用指针强转给子类指针
pp = &pobj;
Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
ps2->_No = 10;
}
we can find out that:
1. Parent class objects cannot be assigned to subclass objects.
2. The parent class object address is forcibly converted to the subclass address, and eventually an out-of-bounds error will occur when the subclass accesses its new members.
4. Inheritance scope
1.Features
2. Test questions
class A
{
public:
void fun(int i)
{
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();
};
// 不定向选择:
// A: 子父func构成重载
// B: 子父func构成隐藏
// C: 程序报错
// D:以上都不对
Answer: BC. B. First, the child and parent classes have member functions with the same name, which constitutes hiding . C, because the subclass hides the function with the same name of the parent class, the matching fun function cannot be found, so an error is reported.
5. Different default member functions for derived classes
This is the experimental code for this time. After this study, we will supplement this derived class.
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; // 姓名
};
class Student : public Person
{
public:
// 构造函数
protected:
int _num; //学号
};
void Test()
{
Student a;
int x = 1;
}
int main()
{
Test();
return 0;
}
1.Constructor
Let's not write the constructor first. What will happen if we look at the default constructor?
It's obvious: with the default constructor, the parent class will call its own constructor; the subclass will also call its own constructor. If there is no constructor, the compiler will automatically generate a constructor. (In short: each constructs its own) and the construction order is parent class first -> subclass.
// 构造函数
Student(const char* name ,const int num = 0)
:Person(name)
,_num(num)
{}
2.Copy construction
Idea: Parent class data, through implicit conversion of the parent class.
// 拷贝构造
Student(const Student& s1)
:_num(s1._num)
, Person(s1) // 通过子类向父类的强转化
{
cout << "Student(const Student& )" << endl;
}
3. Assignment symbol overloading
Idea: Call the parent class assignment symbol overload, and then assign values to the subclass member variables.
// 赋值符号重载
Student& operator=(const Student& s1)
{
cout << "operator=" << endl;
if (this != &s1)
{
Person::operator=(s1);
_num = s1._num;
}
return *this;
}
Supplement: If the subclass member variables do not require deep copying , there is actually no need to write copies and assignment symbols to overload functions, because the parent class has these two functions.
4. Destructor
There is nothing to say about this. The parent and child classes each call their own destructors.
Destruction sequence: The subclass is destructed first, and then the parent class destructor is called after the subclass destruction is completed.
5. Summary
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, it must be called explicitly 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 copy of the base class.4. The destructor of the derived class will automatically call the destructor of the base class to clean up the base class members after being called. Because this can ensure that the derived class object first cleans up the derived class members and then cleans up the base class members in order.5. When initializing a derived class object, first call the base class constructor and then the derived class constructor.6. When destructing and cleaning up the derived class object, first call the destructor of the derived class and then call the destructor of the base class.7. Because the destructor in some subsequent scenarios needs to be rewritten, one of the conditions for rewriting is that the function names are the same (we will explain this later). Then the compiler will perform special processing on the destructor name and change it to destructor(). Therefore, when the parent class destructor does not add virtual, the subclass destructor and the parent class destructor form a hidden relationship.
6. Friendship and inheritance
If you are unfamiliar with Youyuan, you can read this article by the little blogger to recall: Detailed explanation of C++ classes and objects (Part 2) - Use code to practice functions_Huaguoshan~~Programmer's Blog-CSDN Blog
Conclusion: Derived classes cannot inherit the friend relationship of the parent class. In other words, friend functions or friend classes cannot directly obtain private or protected member variables and functions of the derived class .
Sample code:
class Student;
class Person
{
public:
friend void Display(const Person& p, const Student& s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl;
cout << s._stuNum << endl;
}
void main()
{
Person p;
Student s;
Display(p, s);
}
Friend relationships cannot be inherited , so if a friend relationship is needed in a derived class, the friend relationship needs to be re-declared .
7. Inheritance and static members
Detailed explanation of basic knowledge about static member variables and functions in C++ classes and objects (Part 2) - Use code to practice functions_Huaguoshan~~Programmer's Blog-CSDN Blog
Phenomenon conclusion: If the base class defines static members, there will be only one such member in the entire inheritance system . No matter how many subclasses are derived, there is only one static member instance.
Sample code:
class Person
{
public:
Person() { ++_count; }
protected:
string _name; // 姓名
public:
static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum; // 学号
};
class Graduate : public Student
{
protected:
string _seminarCourse; // 研究科目
};
void TestPerson()
{
Student s1;
Student s2;
Student s3;
Graduate s4;
cout << " Person接口 人数 :" << Person::_count << endl;
Student::_count = 666; // 在Student类,访问Person类中静态成员变量,并且设置该静态成员变量
cout << " Person接口 人数 :" << Person::_count << endl;
cout << " Student接口 人数 :" << Student::_count << endl;
cout << " Graduate接口 人数 :" << Graduate::_count << endl;
}
2. Thinking: How to make a class that cannot be inherited
Idea: privatize one of the constructor and destructor.
class Person
{
public:
~Person()
{}
private:
Person()
{}
int _age = -1;
char* _name;
};
class Student : public Person
{
private:
int _score;
};
void TestPerson()
{
Student a;
Person a1; // 当然,父类自己也实例不了对象了
}
No, this way the subclass cannot adjust the parent class. However, at the same time, the parent class itself cannot instantiate objects. You really made me cry to death.
Solution: Create a static member function so that the object can be instantiated outside the class, thus bypassing the system calling the constructor when creating the object.
// 单纯的成员函数也不行,因为对象都没有你告诉咋调函数。
static Person create_Person()
{
return Person();
}
8. Diamond inheritance and virtual diamond inheritance
1. The problem of diamond inheritance
Causes memory waste! ! !
2. Virtual inheritance solution
Experimental code:
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;
cout << &(d.B::_a) << endl;
d.C::_a = 2;
cout << &(d.C::_a) << endl;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
Virtual diamond inheritance allows a class to inherit from multiple base classes through virtual inheritance. Only one common base class sub-object will be retained, avoiding the occurrence of multiple identical base class sub-objects.
No, data redundancy:
3. Low-level details of virtual inheritance
![](https://img-blog.csdnimg.cn/ff698dca00124490817454cff1dae73b.png)
Knowledge of memory endianness will be mentioned here. You can refer to this article [C Language] Integration, floating point data storage, big and small endian. Full of details! ! _Little endian floating point array_Huaguoshan~~Programmer’s Blog-CSDN Blog
Principle simplified diagram:
Supplement: If there are multiple objects of the same virtual diamond inheritance class, the offset tables they access are the same set. For example: D is a class inherited from virtual diamond, then D d1, d2, d3..., d1, d2, d3 all access the same set of offset tables. From here we can also find that in order to solve the problem of data redundancy and ambiguity, virtual diamond inheritance needs to access the offset table, but there is no doubt that this will cause performance loss.
4. The diamond shape is inherited to Xiaobai who doesn’t know whether to live or die, which is a little shocking!
Q: Please explain the printing order
using namespace std;
class A {
public:
A(const char* s) { cout << s << endl; }
~A() {}
};
class B :virtual public A
{
public:
B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class C :virtual public A
{
public:
C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class D :public B, public C
{
public:
D(const char* s1, const char* s2, const char* s3, const char* s4) : B(s1, s2), C(s1, s3), A(s1)
{
cout << s4 << endl;
}
};
int main()
{
D* p = new D("class A", "class B", "class C", "class D");
delete p;
return 0;
}
Analysis: First, the constructor constructs the parent class first, so class A is constructed first , and then according to the inheritance order
Then comes the Class B construct, then Class C, and finally Class D. Class A is only called once during the entire process. (When I first saw it, people were a little confused)
9. Inheritance and combination
Inheritance is a relationship in which one class (called a child or derived class) can inherit the properties and methods of another class (called a parent or base class).
Composition is another type of relationship in which one class (called a container class) contains objects of another class (called a member class). A container class uses the properties and methods of a member class by creating an object of the member class.
(Just like the vector class in STL we learned before, the container contains the string class)
Summarize:
Many people say that C++ syntax is complicated, but in fact multiple inheritance is a manifestation of it. With multiple inheritance , there is diamond inheritance, and with diamond inheritance, there is diamond virtual inheritance. 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 will be problems in complexity and performance. Multiple inheritance can be considered one of the shortcomings of C++. Many later OO languages do not have multiple inheritance, such as Java.
Conclusion
That’s it for this section. Thank you for browsing. If you have any suggestions, please leave them in the comment area. If you bring something to your friends, please leave a like. Your likes and attention will become a blog post . Main motivation for creation