标签搜索

目 录CONTENT

文章目录

C++中的继承(二).md

小小城
2021-08-22 / 0 评论 / 0 点赞 / 9 阅读 / 2,814 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-02,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

C++中的继承(二)

@[toc]

一、派生类的默认成员函数

前面已经总结过类中的默认的成员函数:
类中的默认的成员函数

所以,既然是默认,就代表如果我们不显示定义,编译器就会默认生成,那么在继承体系中,派生类的默认成员函数又是什么yang的呢

  1. 生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用 。( 一旦基类显示定义非默认(非全缺省)的构造函数,编译器将不再自动调用,需要用户在派生类的初始化列表中显示调用基类的构造函数。而如果基类没有显示定义构造函数,子类可以不用定义,让编译器直接调用基类的默认构造函数即可)
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制

(对于2和3两条,如果继承体系中不涉及资源管理问题,用户可以不用显示提供,但是一旦涉及资源管理问题,就必须显示提供,否则就很容易发生浅拷贝问题,从而导致资源泄漏)

  1. 生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
  2. 派生类对象初始化先调用基类构造再调派生类构造。
  3. 生类对象析构清理先调用派生类析构再调基类的析构

代码:

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);
			//这里不能写成*this=s--->会引发无穷递归
			_num = s ._num;
		}
		return *this ;
	}
	~Student()
	{
		cout<<"~Student()" <<endl;
	}
protected :
	int _num ; //学号
};

void Test ()
{
	Student s1 ("jack", 18);
	Student s2 (s1);
	Student s3 ("rose", 17);
	s1 = s3 ;
}

二、探讨派生类和基类的构造函数和析构函数真正的执行顺序

  •  1.从函数体的执行顺序(打印日志)来看:

在这里插入图片描述
先执行基类的构造--->子类的构造--->派生类行为--->派生类的析构--->基类析构

  •  从函数调用角度:

先调用派生类的构造--->在派生类的构造函数的初始化列表位置调用基类的构造--->派生类的行为--->派生类的析构--->基类析构

所以从不同的角度看到的执行顺序是不一样的,需要注意

三、几个特殊的继承

  •  1.友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
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);
}
  •  **2.继承与静态成员
  • 基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例
  • 说明静态的成员是可以被继承的,但是基类和派生类访问的都是同一个
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 ::_count << endl;
	Student ::_count = 0;
	cout <<" 人数 :"<< Person ::_count << endl;
}
0

评论区