类---继承性

类的继承性

·类的继承性含义:
在已有类的基础上扩展出新类

·提出类的继承性的目的(作用):
代码重用,即可以减少重复代码的编写工作。

·类的继承分类:
单一继承和多重继承
//单一继承:派生类仅由一个基类派生。
//多重继承:派生类由多个基类派生

·类的继承方式:
公有继承(派生)、私有继承(派生)、保护继承(派生)
//继承和派生是一对相对的关系

单一继承的图示:
在这里插入图片描述
B—>A
//注意箭头指向!
A称为“基类”或“父类”【BaseClass】
B称为“派生类”或“子类”【DerivedClass】
//没有“继承类”

继承原则:

原则一:
无论何种继承方式,基类的private成员在派生类中均不可直接访问。
原则二:
公有继承:public—>public
protected---->protected
私有继承:public—>private
protected—>private
保护继承:public---->protected
protected—>protected
原则三:
关于访问基类成员的权限:
在派生类内,可以直接访问除基类成员的私有成员外的一切成员;
在派生类外(一般指在主函数或全局里),只能通过派生类的对象名访问公有继承情况下基类的公有成员。

以上的所谓“继承原则”是博主学习类时自己总结的
博主b话:

个人觉得要学好继承,一定要理解掌握好以上(在基类和继承类之间)的转化关系,这样我们在读或者编写用到类的继承知识的代码时,可以自己在脑海里做这样的处理:把基类里的成员相应地转化进派生类中(按照上述的转化关系),从而把两个或两个以上的类当作一个类来看,有利于我们快速读懂或正确编写代码。

读者可能想问:那么如何访问到基类的私有数据成员呢?
解决方法:通过基类的公有函数接口。

继承的作用:继承是软件重用的基础。
公有继承(派生):
1.掌握格式:

class A
{
	...
};

class B:public A //公有继承A 
{
	...
};

2.掌握类的成员的访问权限
3.掌握如何初始化基类的私有数据成员:
在派生类的构造函数的成员初始化列表+使用类名
样例代码:

/*定义"点"类 Point,由"点"类公有派生出"圆"类Circle*/
#include <iostream>
#include <iomanip>
using namespace std;

const double pi = 3.14159;
class Point
{
	private:
		int x, y;
	public:
		Point(int a = 0, int b = 0) //缺省构造函数 
		{
			x = a;
			y = b;
		}
//以上的缺省构造函数亦可写成下面的形式: 
//		Point(int a = 0, int b = 0):x(a), y(b)	{} //总结:在初始化列表初始化数据成员//对本类数据成员进行初始化,使用本类数据成员名 
//		                                           //对对象成员进行初始化,使用对象成员名
		void Setxy(int t1, int t2)
		{
			x = t1;
			y = t2;
		}
		int Getx()
		{
			return x; 
		}
		int Gety()
		{
			return y;
		}
		void Move(int t1, int t2)
		{
			x += t1;
			y += t2;
		}
};

class Circle:public Point
{
	private:
		int r;
	public:
		Circle(int a, int b, int ra):Point(a, b) //派生类的构造函数:对基类进行初始化+使用基类名 
		{
			r = ra; //+需要初始化派生类的数据成员
		}
		void Setr(int ra)
		{
			r = ra;
		}
		void ShowCircle()
		{
			double d = r * r * pi;
			cout << "展示圆的参数:" << endl; 
			cout << "Point:(" << Getx() << ',' << Gety() << ')' << endl; //在类内直接访问基类的公有成员函数+通过基类的公有函数接口访问到基类的私有数据成员 
			cout << "Radius: " << r << '\t' << "Area: " << d << endl;
		}
};

int main()
{
	Circle c(0, 0, 2);
	c.ShowCircle();
	c.Move(2, 2); //思考总结:对私有数据成员进行操作的函数,函数应该放在私有数据成员所在的类里。 
	c.ShowCircle();
	c.Setxy(0, 0); //在类外,通过派生类的对象名访问基类的公有成员函数 
	c.Setr(1);
	c.ShowCircle();
	return 0;
}

运行结果:

展示圆的参数:
Point:(0,0)
Radius: 2       Area: 12.5664
展示圆的参数:
Point:(2,2)
Radius: 2       Area: 12.5664
展示圆的参数:
Point:(0,0)
Radius: 1       Area: 3.14159

派生类的构造函数需要完成的事
1.初始化基类的数据成员
2.初始化派生类的数据成员

私有继承(派生):
1.掌握格式:

class A
{
	...
};

class B:private A //私有继承A
{
	...
};

2.掌握在类内类外,类的成员的访问权限

保护继承(派生):
1.掌握格式:

class A
{
	...
};

class B:protected A //保护继承A
{
	...
};

2.掌握在类内类外,类的成员的访问权限
//私有继承和保护继承用的不多。

private VS protected
1.相同点:private成员和protected成员在类内能直接访问,在类外不可直接访问。
2.不同点:(前提:单一继承的情况下),基类的private成员,无论何种继承方式,都无法在派生类中被直接访问;而 基类的protected成员,无论何种继承方式,在派生类中都可以被直接访问。
//注意:(多重继承的情况下),对于私有继承(派生),基类的protected成员无法在派生类的派生类被直接访问。
//在继承或派生链中,一旦出现私有继承,基类成员的“类内直接访问特性”就无法在派生类中继续传递下去。(基类的“类内直接访问特性”可以传递给派生类,但不能传递给派生类的派生类)
//在类的继承体系中,将类的数据成员的访问权限定义为protected较好。
//protected成员的优点是,既可以在本类中实现数据的隐藏(类内可直接访问、类外不可被直接访问),又可以将类内直接访问特性传递到派生类中。

多重继承
1.格式

class <派生类名>:[<继承方式1>] 基类1, [<继承方式2>] 基类2, ..., [<继承方式n>] 基类n名 
{
	//定义派生类新成员 
	[[private:]
	...]
	[protected:
	...]
	[public:
	...]
};

以下代码帮助大家理解protected和private的不同点以及多重继承

/*先定义点类 Point 和半径类 Radius,再由Point类和Radius多重派生出圆类 Circle*/
#include <iostream>
using namespace std;

const double pi = 3.14159;
class Point
{
	private:
		int x, y; //x、y被定义为private类 
	public:
		Point(int a = 0, int b = 0)
		{
			x = a;
			y = b;
		}
		void Setxy(int k, int t)
		{
			x = k;
			y = t; 
		}
		void Move(int m1, int m2)
		{
			x += m1;
			y += m2;
		}
		int Getx()
		{
			return x;
		}
		int Gety()
		{
			return y;
		}
};

class Radius
{
	protected:
		int r; //r被定义为protected类 
	public:
		Radius(int ra = 0)
		{
			r = ra;
		}
		void Setr(int k)
		{
			r = k;
		}
		int	Getr()
		{
			return r;
		}
};

class Circle:public Point, public Radius
{
	public:
		Circle(int a = 0, int b = 0, int ra = 0):Point(a, b), Radius(ra) {}
		void ShowCircle()
		{
			double ans = r * r * pi; //在基类Radius中,r是protected成员,在派生类Circle中可以直接被访问 
			cout << "展示一个圆:" << endl;
			cout << "Point:(" << Getx() << ',' << Gety() << ')' << endl; //x、y是基类Point的private(私有)成员,在派生类Circle中不可直接被访问 
			cout << "Radius: " << r << '\t' << "Area: " << ans << endl;
		}
};

int main()
{
	Circle c(0, 0, 2);
	c.ShowCircle();
	c.Move(2, 2);
	c.ShowCircle();
	c.Setxy(0, 0);
	c.Setr(1);
	c.ShowCircle();
	return 0;
}

运行结果:

展示一个圆:
Point:(0,0)
Radius: 2       Area: 12.5664
展示一个圆:
Point:(2,2)
Radius: 2       Area: 12.5664
展示一个圆:
Point:(0,0)
Radius: 1       Area: 3.14159

该例中的各个类的关系图示:
在这里插入图片描述
//可能有读者想问:r被定义为protected成员,在派生类中可直接被访问(单一继承时) ,为什么还要定义一个访问r的公有函数接口Getr()呢,是不是有点累赘?答:不累赘的,r被定义为protected成员,虽然在派生类内可以直接被访问,但在类外(如主函数中)依然不能被直接访问,所以定义该公有函数Getr()是为了实现在类外(如主函数中)间接访问到数据成员r 。

//将基类的数据成员定义成保护成员,这样既可以在基类中隐藏数据,又可以在派生类中直接访问这些数据成员。这样处理使程序书写简单,代码执行效率提高。

现在,我们来讨论一下派生类的构造函数和析构函数的执行顺序:
假设派生类的构造函数定义如下:

ClassName(args):Base1(arg_1), Base2(arg_2), ..., Basen(arg_n)
{
	<派生类自身构造函数的函数体> 
};

派生类的构造函数执行顺序
先依次调用基类构造函数Base1()、Base2()、…、Basen(),再执行派生类自身构造函数的函数体。
派生类析构函数调用顺序:首先执行派生类的析构函数的函数体,再依次执行Basen、…、Base2、Base1。即与派生类构造函数调用顺序相反。

关于继承的其他一些问题:
基类的析构函数和构造函数不会被继承,只能在派生类的构造函数或析构函数中自动被调用,完成对基类数据成员的初始化或清理工作。

/*多重继承时,基类构造函数和析构函数的调用顺序*/
#include <iostream>
using namespace std;

class Base1
{
	protected:
		int data1;
	public:
		Base1(int x = 0)
		{
			data1 = x;
			cout << "Base1 Constructor\n";
		}
		~Base1()
		{
			cout << "Base1 Destructor\n";
		}
};
class Base2
{
	protected:
		int data2;
	public:
		Base2(int y = 0)
		{
			data2 = y;
			cout << "Base2 Constructor\n";
		}
		~Base2()
		{
			cout << "Base2 Destructor\n";
		}
};

class Derived:public Base1, public Base2
{
	private:
		int data3;
	public:
		Derived(int x, int y, int z):Base2(y), Base1(x)
		{
			data3 = z;
			cout << "Derived Constructor\n";
		}
		~Derived()
		{
			cout << "Derived Destructor\n";
		}
		void Show()
		{
			cout << data1 << ',' << data2 << ',' << data3 << endl; 
		}
};

int main()
{
	Derived d(1, 2, 3);
	d.Show();
	return 0;
}

运行结果:

Base1 Constructor    //调用了基类Base1的构造函数
Base2 Constructor    //调用了基类Base2的构造函数
Derived Constructor  //调用了派生类Derived的构造函数
1,2,3                
Derived Destructor   //调用了派生类的析构函数
Base2 Destructor     //调用了基类Base2的析构函数
Base1 Destructor     //调用了基类Base1的析构函数

/*基类成员、对象成员的构造函数和析构函数的调用顺序*/
#include <iostream>
using namespace std;

class Base1
{
	protected:
		int data1;
	public:
		Base1(int x = 1)
		{
			data1 = x;
			cout << data1 << ", Base1 Constructor\n";
		}
		~Base1()
		{
			cout << data1 << ", Base1 Destructor\n";
		}
};

class Base2
{
	protected:
		int data2;
	public:
		Base2(int y = 2)
		{
			data2 = y;
			cout << data2 << ", Base2 Constructor\n";
		}
		~Base2()
		{
			cout << data2 << ", Base2 Destructor\n";
		}
};

class Derived:public Base1, public Base2      //A
{
	private:
		int data3;
		Base1 b1;   //B
		Base2 b2;
	public:
		Derived(int x, int y, int z):Base1(x), Base2(y), b1(x + y), b2(x + z)
		{
			data3 = z;
			cout << data3 << ", Derived Constructor\n";
		}
		~Derived()
		{
			cout << data3 << ", Derived Destructor\n";
		}
		void show()
		{
			cout << data1 << ',' << data2 << ',' << data3 << endl;
		}
};

int main()
{
	Derived d(1, 4, 9);
	d.show();
	return 0;
}

运行结果:

1, Base1 Constructor      //初始化基类Base1
4, Base2 Constructor      //初始化基类Base2
5, Base1 Constructor      //初始化对象成员b1
10, Base2 Constructor     //初始化对象成员b2
9, Derived Constructor    //初始化派生类Derived
1,4,9
9, Derived Destructor     //析构派生类Derived
10, Base2 Destructor      //析构对象成员b2
5, Base1 Destructor       //析构对象成员b1
4, Base2 Destructor       //析构基类Base2
1, Base1 Destructor       //析构基类Base1

1.先调用基类的构造函数,再调用对象成员的构造函数,最后执行派生类自身构造函数的函数体。(这句话一定要记牢、记死;经常考经常考!)
2.析构函数的调用顺序与构造函数的调用顺序相反。
3.调用基类构造函数的顺序是由A行继承顺序决定;调用对象成员构造函数的顺序是由B行对象定义决定。

class UStudent:public Student
{
private:
string major;
};

UStudent::UStudent(int number1, string name1, float score1, string major1):Student(number1, name1, major1), major(major1)
{
major = major1; //major会被赋值两次
}
//虚基类
class A
{

};

class B:virtual public A
{

};

class C:public virtual A
{

};

class D:public B, public C //D中只有一份A
{

};

class A
{
public:
int x;
A(int a = 0)
{
x = a;
}
};

class B:public A //类的继承
{
int y;
A a1; //类的组合 (组合关系也可以起到代码重用)
public:

};

//类的 组合和 继承:是代码重用的两种重要方法
//sizeof一个B类对象时,值为12

//区分什么时候用组合什么时候用继承

//尽量重用代码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值