• C++之继承<2>【详解】


    1. 派生类的默认成员函数

    1.1 1. 构造成员函数

      派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

      无论是否显示的调用基类的构造成员函数,都会自动调用基类的默认成员函数:

    #include 
    using namespace std;
    class Person
    {
    public:
    	Person(const char* name = "peter")
    		: _name(name)
    	{
    		cout << "Person()" << endl;
    	}
    	
    	string _name; 
    };
    
    
    class Student : public Person
    {
    public:
    	Student(const char* name, int num)
    		: _num(num)
    	{
    		cout << "Student()" << endl;
    	}
    protected:
    	int _num; 
    };
    int main()
    {
    	Student s1("jack", 18);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    没有显示调用
    显示调用后:
    在这里插入图片描述

    上述的后半段的意义是:如果基类没有默认的构造函数,那么是这样的:

    	Person(const char* name = "peter")
    		: _name(name)
    	{}
    
    • 1
    • 2
    • 3

      可以进行传参来构造对象,如果你在派生类没有显示的调用它,那么不能进行进行传参来构造。

    #include 
    using namespace std;
    
    class Person
    {
    public:
    	Person(const char* name = "peter")
    		: _name(name)
    	{
    		cout << "Person()" << endl;
    	}
    	
    	string _name; 
    };
    
    
    class Student : public Person
    {
    public:
    	Student(const char* name, int num)
    		: _num(num)
    	{
    		cout << "Student()" << endl;
    	}
    protected:
    	int _num; 
    };
    int main()
    {
    	Student s1("jack", 18);	
    	cout << s1._name; 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述
    上图可以看到,传入的参数是“jack”, 但是构造出来的对象属性是“peter”。
      至于必须在初始化列表显示的调用,是因为祖师爷定下的规则是,先构造基类再构造派生类,初始化列表是先于构造函数执行的。
      Person(name)在初始化列表中的顺序可以随意改动的,因为初始化列表的执行顺序只跟声明的顺序有关,跟初始化列表中的先后顺序无关。

    1.2 拷贝复制

    分别是拷贝构造函数和operator=复制函数:

    1. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
    2. 派生类的operator=必须要调用基类的operator=完成基类的复制。

    上面两条的原因和构造函数的一样,就不在赘述。
    下面是验证的代码:

    #include 
    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;
    	}
    protected:
    	string _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;
    	}
    protected:
    	int _num; //学号
    };
    int main()
    {
    	Student s1("jack", 18);
    	Student s2(s1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    1.3 构造函数和析构函数的执行顺序

    1. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
    2. 派生类对象初始化先调用基类构造再调派生类构造。
    3. 派生类对象析构清理先调用派生类析构再调基类的析构。
    • 首先,为什么一定先调用基类构造函数再调用派生类的的构造函数呢?
        如果你先调用派生类的构造函数,派生类是继承基类的,那么派生类中就可以使用基类中的属性和行为,但是此时还没有调用基类的构造函数,所以不能这样。

    • 为什么一定先调用派生类的析构函数再调用基类的析构函数呢?
        如果先调用基类的析构函数的话,会释放掉一些变量或指针,那么派生类使用继承过来的这些变量或者指针的时候,它们已经变成了野指针,因此不能如此。

    在这里插入图片描述

    下面是完整代码,大家可以尝试验证:

    #include 
    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; // 姓名
    };
    
    
    
    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; //学号
    };
    int main()
    {
    	Student s1("jack", 18);
    	Student s2(s1);
    	Student s3("rose", 17);
    	s1 = s3;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    2. 继承和友元

    友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

    #include 
    using namespace std;
    
    class Student;
    class Person
    {
    public:
    	friend void Display(const Person& p, const Student& s);
    protected:
    	string _name = "zhangsan"; // 姓名
    };
    
    
    
    class Student : public Person
    {
    public:
    	
    protected:
    	int _num; //学号
    };
    
    void Display(const Person& p, const Student& s)
    {
    	cout << p._name << endl;
    	cout << s._num << endl;
    }
    int main()
    {
    	Student s;
    	Person p;
    	Display(p, s);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在这里插入图片描述

    从上面图中可以看出。

    3. 继承与静态成员

      基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

    #include 
    #include 
    using namespace std;
    
    
    class Person
    {
    public:
    	Person()
    	{
    		++_count;
    		++age;
    	}
    
    
    public:
    	static int _count;
    	int  age = 0; // 姓名
    };
    int Person::_count = 0;
    
    
    class Student : public Person
    {
    public:
    
    protected:
    	int _num; //学号
    };
    
    int main()
    {
    	Student s;
    	Person p;
    	cout << "Person::_count:  "<<Person::_count<<endl;
    	cout << "Person::age:  "<<p.age<<endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    运行结果是:
    在这里插入图片描述

      由此可见,静态成员_count是共有的,只有一个。


      😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄

  • 相关阅读:
    jvm与锁
    Impact of Problem Decomposition on Cooperative Coevolution
    【MATLAB】求解含有三角函数的方程
    爬虫工程师基本功,前端基础
    12、乐趣国学—践行《弟子规》的“信”懂得处世之道(下篇)
    配置hadoop模板虚拟机
    vue165-main.js-vue中的小提示
    持续的敏捷转型--我们的经验
    Swin Transformer网络模型
    2023年【熔化焊接与热切割】考试试卷及熔化焊接与热切割试题及解析
  • 原文地址:https://blog.csdn.net/tang20030306/article/details/133954385