继承的总结

1.继承与派生的概述

什么是继承?
说到继承,类似于现实生活中一个人生前有很多财富,那么他离世后他的所有东西都归属他的亲人,他的亲人拥有他的所有财富,吧他的资产继承下来。

继承就是吧你所拥有的东西,通过继承,然后这些东西也就可以属于我了,既然归属于我,那么我就可以去使用这些继承下来的东西了,我就有使用的权力,权限。

什么是派生?
小明生了一个儿子叫小红,只有通过小明派生出一个小红,也就是小红只能通过小明派生的,不能通过其他人出来个小红,或者也不能说小红是自己蹦出来,而是通过谁才能产生小红,是固定的一对一关系。

而在类里面,类的继承是指的是:新类通过一个已经存在的类得到已有的特性。
从已经有的类得到新类的过程叫派生。

类的派生也有好处:如果需要在原有的功能基础上增加一些功能,那么我可以通过派生出一个类,派生出来的类就是保留原来功能基础上增加一些新的功能特性。

已有的类称为基类(父类),通过已经存在的类生成出来的类叫派生类(子类)

2.派生类的声明

派生类的声明格式:

class 派生类名:继承方式 基类名
{
   //新增加的成员函数与数据
}

为了方便理解,举个事例。
代码:

#include <iostream>
using namespace std;
class Person //人
{
private:
	char name[10]; //名字
	int age;       //年龄

public:
	Person(const char* Nm, int ag) : age(ag) { strcpy(name, Nm); }
	void Show() const
	{
		cout << "名字: " << name << endl;
		cout << "年龄: " << age << endl;
	}
};
int main()
{
	Person p("小明", 18);
	p.Show(); 

	system("pause");
	return 0;
}

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

定义的一个人,类,这个人包含人的姓名,人的年龄,但如果在已有的属性上增加一些属性。
示例:

#include <iostream>
using namespace std;
class Person //人
{
private:
	char name[10]; //名字
	int age;       //年龄

public:
	Person(const char* Nm, int ag) : age(ag) { strcpy(name, Nm); }
	void Show() const
	{
		cout << "名字: " << name << endl;
		cout << "年龄: " << age << endl;
	}
};
class Student: public Person  //公有方式继承人类
{
private:
	int num;
public:
	Student(const char* Nm, int ag, int nm) : Person(Nm, ag), num(nm) {}  //构造函数
	void Set(int n) { num = n; }
	int Get() const { return num; }
};
int main()
{
	Student STD("小红", 18, 1001);
	STD.Show();
	cout << "学号: " << STD.Get() << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
这里通过人这个类本来存在的名字和年龄,后来想要增加一个属性为学号,就派生出一个学生类,学生类增加了一个属性为学号,并且还保留了名字和年龄的属性。

通过学生类以公有方式继承人这个类,不但保留了原本的数据属性而且还增加了一个学号的属性的特点

在这里插入图片描述

class Student: public Person  //公有方式继承人类

其中student后跟的冒号后面,跟着关键字public与类名Person,这表示Student将继承类Person的特性。
其中Person直接基类,简称父类
类Student直接派生类,简称子类。
关键字public指出派生的方式,告诉编译器Studnet通过Student公有方式派生

 一个派生类只有一个直接基类的情况,称为单继承。一个派生类拥有多个直接基类称为多继承。
其中”基类名“是一个已经定义的类的名称,”派生类名“是继承原有类的特征而生存的新类的名字。

公有继承:

class Studentpublic Person
{
   ...
}

私有继承:

class Studentprivate Person
{
   ...
}

保护继承:

class Studentprotected Person
{
    ...
}

如果以下这种方式:

class Student:Person
{
    ...
}

不给出继承的方式,默认是私有继承。

3.继承的方式与访问权限

继承有三种继承方式:公有,私有,保护。

公有继承:基类的公有数据属性与保护数据属性,在派生类中依然保持为公有属性与保护属性,基类的私有的数据属性在派生类不可访问。

私有继承:基类的公有数据属性与保护数据属性,在派生类中变为私有数据属性,基类的私有数据属性在派生类中不可访问。

保护继承:基类的公有数据属性与保护数据属性,在派生类中变为保护数据属性,基类的私有数据属性在派生类中不可访问。

所以不管哪种继承都不能通过子类去访问基类的私有数据属性的成员,但是3种继承都可以去访问父类的保护数据成员与公有数据成员。

3.1公有继承

公有继承图解访问权限
在这里插入图片描述
示例:

#include <iostream>
using namespace std;
class Person //基类
{
private:     //私有
	int a;
public:      //公有
	int b;
protected:   //保护
	int c;

public:
	Person(int a1, int b1, int c1) : a(a1), b(b1), c(c1) {} //构造
};
class Student: public Person  //公有方式继承人类
{
private:
	int d;
public:
	Student(int a1, int b1, int c1, int d1) : Person(a1, b1, c1), d(d1) {}  //构造
	void Show() const
	{
		//cout << a << endl;  错, 因为a在基类里面是私有成员,派生类不能访问
		cout << "b = " << b << endl;  
		cout << "c = " << c << endl;
		cout << "d = " << d << endl;
	}
};
int main()
{
	Student A(1, 2, 3, 4);
	A.Show();
	system("pause");
	return 0;
}

3.2私有继承

私有继承图解访问权限
在这里插入图片描述
示例:

#include <iostream>
using namespace std;
class Person //基类
{
private:     //私有
	int a;
public:      //公有
	int b;
protected:   //保护
	int c;

public:
	Person(int a1, int b1, int c1) : a(a1), b(b1), c(c1) {} //构造
};
class Student : private Person  //公有方式继承人类
{
private:
	int d;
public:
	Student(int a1, int b1, int c1, int d1) : Person(a1, b1, c1), d(d1) {}  //构造
	void Show() const
	{
		//cout << a << endl;  错, 因为a在基类里面是私有成员,派生类不能访问
		cout << "b = " << b << endl;
		cout << "c = " << c << endl;
		cout << "d = " << d << endl;
	}
};
int main()
{
	Student A(1, 2, 3, 4);
	A.Show();
	system("pause");
	return 0;
}

3.3保护继承

保护成员与私有成员类似,不能被类外的非友元函数访问,但是与私有成员不同的是保护成员可以被派生类的成员函数或友元函数访问
保护继承图解访问权限
在这里插入图片描述

示例:

#include <iostream>
using namespace std;
class Person //基类
{
private:     //私有
	int a;
public:      //公有
	int b;
protected:   //保护
	int c;

public:
	Person(int a1, int b1, int c1) : a(a1), b(b1), c(c1) {} //构造
};
class Student : protected Person  //公有方式继承人类
{
private:
	int d;
public:
	Student(int a1, int b1, int c1, int d1) : Person(a1, b1, c1), d(d1) {}  //构造
	void Show() const
	{
		//cout << a << endl;  错, 因为a在基类里面是私有成员,派生类不能访问
		cout << "b = " << b << endl;
		cout << "c = " << c << endl;
		cout << "d = " << d << endl;
	}
};
int main()
{
	Student A(1, 2, 3, 4);
	A.Show();
	system("pause");
	return 0;
}

三种继承方式运行结果相同:
在这里插入图片描述

总结:
三种继承后,子类都可以访问基类的保护成员与公有成员,子类不能访问基类的私有成员。
如果是以公有方式继承下来,那么基类的公有属性的数据和保护属性的数据相当于全部
拷贝到子类去,属性不变

如果是以私有方式继承下来,那么基类的公有属性的数据和保护属性的数据相当于全部
拷贝到子类去,属性统一改为私有属性方式
如果是以保护方式继承下来,那么基类的公有属性的数据和保护属性的数据相当于全部
拷贝到子类去,属性统一改为保护属性方式

4.多继承

可以访问示例:

#include <iostream>
using namespace std;
class A  //父类A
{
protected:
	int a;
public:
	A(int a1) : a(a1) {}
};
class B :public A //类B以公有方式继承类A
{
	//此时B类以公有方式继承类A
	//那么类A的保护成员数据拷贝的类B里面来,数据属性还是保护属性
public:
	B(int a1) : A(a1){}
};
class C : protected B //类C以保护方式继承类B
{
	//此时C类以保护方式继承类B
	//那么类B的保护成员数据拷贝的类C里面来,数据属性还是保护属性
public:
	C(int a1) : B(a1) {}
	void Show() const { cout << "a = " << a << endl; } //可以访问
};
int main(int argc, char* agrv[])
{
	C M(1);
	M.Show();
	system("pause");
	return 0;
}

不可访问的示例:

#include <iostream>
using namespace std;
class A  //父类A
{
protected:
	int a;
public:
	A(int a1) : a(a1) {}
};
class B :private A //类B以公有方式继承类A
{
	//此时B类以私有方式继承类A
	//那么类A的保护成员数据拷贝的类B里面来,数据属性变成了私有属性
public:
	B(int a1) : A(a1){}
};
class C : protected B //类C以保护方式继承类B
{
	//此时C类以保护方式继承类B
	//但由于类B中通过私有方式继承类A,此时类B的数据只有一个私有的数据属性
	//但由于私有属性的数据不能访问,所以数据到达类B后,就停止了
public:
	C(int a1) : B(a1) {}
	void Show() const { cout << "a = " << a << endl; } //不可以访问
};
int main(int argc, char* agrv[])
{
	C M(1);
	M.Show();
	system("pause");
	return 0;
}

多继承示意图:
在这里插入图片描述

基类类A,属性有个保护属性和公有属性数据,
然后A类以公有方式派生出类B,
那么将类A里面的保护属性和公有属性数据拷贝到类B去,到达类B后,属性不变,公有还是公有,保护还是保护属性。那么B类可以去访问从A类继承下来的数据了。
然后类B以保护方式派生出类C
那么B类里面的数据全部拷贝到类C去,访问权限改为保护。那么C类可以去访问B类继承下来的数据了。
但由于B类的数据是以公有方式从A类继承下来的,所以C类可以去访问A类的数据。间接访问。

但如果到达B类就停止了衍生新的类了。
如下这种情况:
基类类A,属性有个保护属性和公有属性数据,
然后A类以私有方式派生出类B,
那么将类A里面的保护属性和公有属性数据拷贝到类B去,到达类B后,数据访问权限全部改为私有了。那么B类可以去访问从A类继承下来的数据了。
然后类B以保护方式派生出类C
但由于B类里面的数据是继承A类后最终都是私有数据属性的,所以C类不能去访问B类。最终到达B类后,就停止了派生了。
但由于B类的数据是从A类以私有方式继承下来的,所以到达B类后就停止了,所以后面的类就不能去访问了A类。

5.派生类的构造函数与析构函数

基类的构造函数不能被派生类所继承,因此在设计派生类的构造函数时,不仅要考虑派生类新增的数据,还要考虑基类数据成员的初始化。就是执行派生类的构造函数时候,同时能初始化基类的数据和派生类的数据。
具体声明格式:
派生类名(形参表) :基类构造函数名(基类的形参表), 数据1(数据成员参数1), 数据2(数据成员参数2), …
{
  … 函数体
}

示例:

#include <iostream>
using namespace std;
class A
{
private:
	int a;
public:
	A(int a) { this->a = a; }
	//A(int a = 0) { this->a = a; } //有默认参数的构造函数
	int Get() const { return a; }
};
class B : public A
{
private:
	int b;
public:
	B(int a1, int b1) :A(b1), b(a1) {} //构造函数
	void Show() const
	{
		cout << "a = " << Get() << endl;  //间接访问父类的私有成员
		cout << "b = " << b << endl;
	}
};
int main(int argc, char* argv[])
{
	B M(1, 2);
	M.Show();
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
但如果基类没有构造函数,或者定义了没有参数的构造函数,或定义的构造函数的所有参数都是默认值。
以上这些情况,可以在构造函数时候不需要给基类进行构造。

示例:

#include <iostream>
using namespace std;
class A
{
private:
	int a;
public:
	//A(int a) { this->a = a; }
	A(int a = 0) { this->a = a; } //有默认参数的构造函数
	int Get() const { return a; }
};
class B : public A
{
private:
	int b;
public:
	B(int a1) :b(a1) {} //构造函数 没有给基类A进行构造
	void Show() const
	{
		cout << "a = " << Get() << endl;  //间接访问父类的私有成员
		cout << "b = " << b << endl;
	}
};
int main(int argc, char* argv[])
{
	B M(1);
	M.Show();
	system("pause");
	return 0;
}

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

5.1构造的先后顺序

代码示例:
 系统在创建派生对象时候,首先去调用基类的构造函数初始化基类数据成员,然后调用派生类对象数据的初始化。

#include <iostream>
using namespace std;
class A
{
public:
	A()
	{
		cout << "A类构造" << endl;
	}
};
class B :public A
{
public:
	B()
	{
		cout << "B类构造" << endl;
	}
	
};
int main()
{
	B obj;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
所以构造顺序为:先基类,后派生类。

5.2析构函数执行顺序

代码示例

#include <iostream>
using namespace std;
class A
{
public:
	~A()
	{
		cout << "A类析构" << endl;
	}
};
class B :public A
{
public:
	~B()
	{
		cout << "B类析构" << endl;
	}

};
int main()
{
	B obj;
	system("pause");
	return 0;
}

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

析构顺序是:先派生类后基类。

6.多继承与虚基类

如果一个派生类拥有两个或者更多的基类,那么这种行为称为多继承,多继承的派生类的声明格式:
class 派生类名: 继承方式1 基类名1, 继承方式2 基类名2
{
  … 派生类新增的数据成员和成员函数
}
例如:声明类A和类B,按如下方式声明多继承的派生类C

class C :public A, private B
{
  ...                    类C新增的数据成员和成员函数
};

上面声明的类C是多继承的派生类,以公有方式继承类A,并且以私有方式继承类B。类C以不同的方式继承了类B和类A。

6.1多继承的构造顺序

示例:

#include <iostream>
using namespace std;
class A
{
public:
	A() { cout << "A类构造" << endl; }
	~A() { cout << "A类析构" << endl; }
};

class B
{
public:
	B() { cout << "B类构造" << endl; }
	~B() { cout << "B类析构" << endl; }
};

class C :public A, private B
{
public:
	C() { cout << "C类构造" << endl; }
	~C() { cout << "C类析构" << endl; }
};
int main(int argc, char* argv[])
{
	C obj;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
构造顺序按照以下声明的语句进行构造执行:

class C :public A, private B

构造先执行A类构造,然后执行B类构造,最后是自己
析构则相反

如果改为:

class C :public B, private A

那么构造顺序和析构顺序都改变
构造先是构造B然后是A最后是C
析构则相反

6.2多继承引起的多义性问题

问题:
有个类A分别派生了类B和类C
类D是通过多继承方式由类B和类C共同派生出来的
如图:
在这里插入图片描述
此时类A有个函数Show()权限是public。
B和C公有方式继承了类A,那么B和C都可以访问类A的Show()函数
但B和C同时以公有方式派生出来了一个D,此时D想要访问A的Show()函数
那么D是通过直接基类B去访问类A的Show()函数, 还是D是通过直接基类C去访问了类A的Show()函数
这就有了个多义性问题,所以不知道D到底是从直接基类B去访问A,还是从直接基类C去访问A,导致编译出现问题。

示例如下:

#include <iostream>
using namespace std;
class A
{
public:
	void Show() const { cout << "多继承的应用示例" << endl; }
};
//B继承A
class B : public A
{};
//C继承A
class C : public A
{};
//D同时继承B和C
class D : public B, public C
{};
int main()
{
	D obj;
	obj.Show(); //错, 不明确Show()是通过哪个直接基类去访问类A
	system("pause");
	return 0;
}

修改:

#include <iostream>
using namespace std;
class A
{
public:
	void Show() const { cout << "多继承的应用示例" << endl; }
};
//B继承A
class B : public A
{};
//C继承A
class C : public A
{};
//D同时继承B和C
class D : public B, public C
{};
int main()
{
	D obj;
	obj.B::Show();  //可以通过直接基类B去访问类A里面的Show()
	obj.C::Show();  //可以通过直接基类C去访问类A里面的Show()
	system("pause");
	return 0;
}

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

6.3虚基类

由于出现了这种多义性问题,虽然上面那种可以解决掉,但是有更好的解决办法,此时引入了虚基类的概念。
使用虚基类,使得在继承间接共同基类只保留了一份成员副本。

假设类D是类B和类C公有派生类,而类B和类C又是类A的派生类,设A的公有成员函数Show(),如果将A定义为B和C的虚基类,则类A的成员函数Show()在类B和类C中只保留一个副本,这样就不会有多义性问题。
虚基类的关键字:virtual
虚基类定义格式:
class 派生类名: virtual 继承方式 基类名

注意:虚基类不是在声明基类时声明,而是在声明派生类时,在继承方式前加关键字virtual关键字。

代码示例:

#include <iostream>
using namespace std;
class A
{
public:
	void Show() const { cout << "多继承示例" << endl; }
};
/*
在类B与类C继承类A时候,前面加了virtual, 表示,类B和类C只保留
一份继承类A的副本
*/
class B : virtual public A 
{};
class C : virtual public A
{};
class D :public B, public C
{};
int main()
{
	D obj;
	obj.Show();
	system("pause");
	return 0;
}

6.4虚基类应用示例:

定义Person(人)类,由Person分别派生出类Teacher(教师)和类Student(学生),再由类Teacher
(教师)和类Student(学生)采用多继承的方式派生出新类Graduate(研究生),各类之间的继承关系如图:
在这里插入图片描述
要求:
1.在类Person中包括的数据成员有姓名(name), 年龄(age), 性别(sex)。
在类Teacher还包括数据成员职称(title)和工资(wage),在类Student类中包括数据成员学号(num)和学分(creditHour)
在类Graduate(研究生)中没有包括新成员

2.在类体定义成员函数

3.每个类都有构造函数与显示信息函数Show()

在本例中将类Person声明为类Studnet和类Teacher的虚基类

示例代码:

#include <iostream>
#include <cstring>
using namespace std;
//基类  人
class Person
{
protected:
	char name[10];  //姓名
	int age;        //年龄
	char sex[10];   //性别
public:
	Person(const char* nm, int ag, const char* sx) : age(ag) 
	{ 
		strcpy_s(name, sizeof(nm), nm); 
		strcpy_s(sex, sizeof(sx), sx);
	}
	void Show() const;
};
void Person::Show() const
{
	cout << "姓名: " << name << endl;
	cout << "年龄: " << age << endl;
	cout << "性别: " << sex << endl;
}

//教师类 公有方式继承人 类
class Teacher : virtual public Person     
{
protected:
	char title[10];  //职称
	float wage;      //工资
public:
	Teacher(const char* nm, int ag, const char* sx, const char* tit, float wg) : Person(nm, ag, sx), wage(wg) { strcpy_s(title, sizeof(tit), tit); }
	void Show() const;
};
void Teacher::Show() const
{
	Person::Show();
	cout << "职称: " << title << endl;
	cout << "工资: " << wage << endl << endl;
}

//学生类以 公有方式继承人 类
class Student : virtual public Person
{
protected:
	long num;                   //学号
	long credithour;            //学分
public:
	Student(const char* nm, int ag, const char* sx, long n, long cdr) : Person(nm, ag, sx), num(n), credithour(cdr) {}
	void Show() const;
};
void Student::Show() const
{
	Person::Show();
	cout << "学号: " << num << endl;
	cout << "学分: " << credithour << endl << endl;
}

//研究生类 以公有方式继承老师类  并且  以公有方式继承学生类
class Graduate : public Teacher, public Student
{
public:
	Graduate(const char* nm, int ag, const char* sx, const char* tit, float wg, long n, long cdr)
		: Person(nm, ag, sx), Teacher(nm, ag, sx, tit, wg), Student(nm, ag, sx, n, cdr) {}
	void Show() const;
}; 
void Graduate::Show() const
{
	Person::Show();
	cout << "职称: " << title << endl;
	cout << "工资: " << wage << endl;
	cout << "学号: " << num << endl;
	cout << "学分: " << credithour << endl;
}
int main()
{
	Teacher t("小明", 48, "男", "教授", 3890);
	Student s("周杰", 26, "男", 201010, 32);
	Graduate g("李靖", 28, "女", "助教", 500, 201011, 33);
	t.Show();
	s.Show();
	g.Show();
	system("pause");
	return 0;
}

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值