1、继承是指函数之间相同性很高时,可以采取从基类(父类)继承相同的部分,形成派生类(子类)。

如图:

继承_父类

可以将三者相同部分写成一个函数然后分别继承

继承_子类_02

2、继承方式

三种:public,private,protected

继承_静态成员_03

3、赋值转换——切片

子类对象可以赋值给 父类的对象 / 父类的指针 / 父类的引用。这里有个形象的说法叫切片或者切割。寓意把子类中父类那部分切来赋值过去。并且这个过程中没有类型转换。没有类型转换也意味着不产生临时变量!,临时变量具有常性,所以不需要加上const。

继承_静态成员_04

下面,我们来看一看代码是怎么体现赋值过程没有类型转换的:

class Person
{
protected:
	string _name; 
	string _sex;  
public:
	int	_age;	 
};
class Student :public Person
{
public:
	int _No;
};
int main()
{
	Person p;
	Student s;
	//中间不存在类型转换
	p = s;
    //引用赋值
	Person& rp = s;
    rp._age = 1;
    //指针赋值
	Person* ptr = &s;
	ptr->_age = 2;
	return 0;
}

继承_静态成员_05

虽然是不同类型的,但是不会发生类型转换,而类型转换在中间会产生临时变量,而临时变量具有常性,必须加const,而对于父类和子类的赋值可以不加const!

赋值转换也称为向上转换,子类可以给父类的对象,指针、引用。而向下装换是不行的:父类对象不能赋值给子类对象,子类有的成员而父类没有,缺少的部分怎么去给

这里先简单了解一下:父类的指针可以通过强制类型转换赋值给子类的指针。但是必须是父类的指针是指向子类对象时才是安全的。这里父类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换。

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;
}

4、继承的作用域——隐藏

在继承体系中基类和派生类都有独立的作用域。而如果子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用基类::基类成员显示访问)

需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

注意在实际中在继承体系里面最好不要定义同名的成员。

class Person
{
protected:
	string _name = "HWC"; 
	int _num = 10086; 
};
class Student : public Person
{
public:
	void Print()
	{
		cout << " 姓名:" << _name << endl;
		cout << " 身份证号:" << Person::_num << endl;
		cout << " 学号:" << _num << endl;
	}
protected:
	int _num = 666; 
};
int main()
{
	Student s1;
	s1.Print();
	return 0;
}

继承_子类_06

_num默认访问就近原则,访问自己的,如果想访问父类的成员,就直接指定作用域Person::_num。

5、总结:

1、基类private成员在派生类中无论以什么方式继承均不可见(在类外与类内均不能直接访问);

2、基类的私有成员在子类均不可见,基类的其他成员在子类的访问方式取权限小的;public>protected>private

3、为使私有成员可以在类内访问,定义为protected(子类可直接手机用)

4、基类与派生类具有赋值兼容转换(特殊的语法规则,不是类型转换)

5、子父类同名函数关系为隐藏

6、子类默认生成构造

  父类成员----->默认构造

  子类成员(内置)----->默认构造

  子类成员(自定义)----->默认构造

子类中的父类成员的构造,拷贝,析构均需要调用父类中的函数


7、友元关系无法继承

eg:你父亲的朋友不一定是你的朋友


8、如果父类中有静态成员,在继承中子类访问该成员时访问的是统一对象

先在类中定义静态变量,然后在外部声明 并进行初始化 , 该步骤的作用是 告诉编译器 , 为 静态变量 分配内存 , 否则不分配内存 编译会报错 ;声明并初始化静态变量,如果没有该操作 , 静态成员不可用之后 , 才能使用 类的 静态成员 , 如果没有在类外部初始化 , 该静态成员是无法使用的 ;

继承_子类_07

9、单继承与多继承(两个及以上的直接父类继承)

      注:多继承看似合理但实际上是个大坑

如:蔬菜和水果同属于植物,西红柿既是蔬菜也是水果。这样西红柿就有了两遍的植物信息,造成数据冗余与二义性

继承_静态成员_08

例如助教既是学生也是老师但其身份证号只有一个,多继承使其储存两遍。

为解决这个问题可以用虚继承(菱形继承):

继承_子类_09

继承_父类_10

virtual必须在第一时间加

10、继承(b继承a)与组合(b含有a)

继承_静态成员_11


https://blog.csdn.net/weixin_60478154/article/details/128859481