C++的public继承中的public、private和protected

    C++语言是对C语言的一种增强,而其主要贡献在于,为C语言增加了类和模板等功能,可以帮助实现面向对象编程和代码复用等更方便的功能。

    C++语言的类,一般都包含两种成员,分别是成员变量和成员函数。成员变量可以用来表示该类的某些状态,而成员函数则可以用来对这些成员变量进行操作。而对于这些成员而言,最核心的概念当属类的封装和继承的概念。

1. 封装

    所谓封装,就是编写一个类对象,只留出用户接口,不需要用户去关心里面的东西是如何构造的,拿来就可以直接使用。这就像一台手机,卖家卖给用户,用户只需要使用手机而不需要知道手机是怎样制造的,同时也不可能拆开看个究竟再自己仿造一个,可以说是一种“黑箱概念”。这既方便了用户的使用,让用户不再挂念内部实现的细节,同时也避免了用户有意地(比如想仿制一个相同的,可能导致知识产权侵权)或无意地(可能导致类的损坏而影响使用)对原有的封装进行修改。

    可以通过一个生活中的简单的例子来理解C++实现封装的方法。手机厂商不允许用户擅自篡改手机系统,但手机厂商必须允许用户对手机在可控的范围内进行操作,比如增加或删除一个软件或电话号码。而这样的增加、删除或修改数据一定是不可以篡改系统的。若要实现这样的效果,唯一的办法就是厂商自己来制定一套手机操作的规则,让用户只能使用他提供的规则去操作,这样就避免了所有的对原封装的篡改。与此原理相似地,为了实现封装,C++提供了在类中的两种操作范围(这个用词可能不太妥当):public和private,中文意思可以称为“公有”和“私有”。public的成员函数都是可以通过类对象来直接访问的,可以通过"class_name.class_method(para1,para2)"的语法格式或者使用类指针以"p_class_name->method(para1.para2)"的形式来实现的。如果将成员变量放在public里边,同样可以利用类似的方法访问,即"class_name.class_member"或"p_class_name->class_member"。但根据C++对类封装的原则,不希望提供给用户随意修改修改类内成员变量的权限,因此,C++语言为类提供了private,就是希望类的编写者把该类所涉及的所有数据都放进这个部分,然后在public中编写一些用户接口(公有成员函数),让用户只能通过这些接口来进行操作。

2.继承

    我之前写过的一篇讲“派生类的重新定义”的文章简要地介绍过“继承”的概念。由于学习进度,我在这里主要想讲一下protected关键字在public继承中的效果(其他类型的继承我还没有学到,后面会继续补充相关的内容)。

    为了更加简明易懂,这里举一个简单的例子来进行说明。

    首先定义一个基类“shape”,它包含"private"、"protected"和"public"类型的成员变量和成员函数,并在main函数中创建一个“shape”类的对象s1并调用成员函数来演示各种类型成员之间的访问权限。

    之后定义一个从基类“shape”派生出来的“rectangle”类,它在基类的基础上,又增加了自己独有的一些public和private成员变量和成员函数,并试图让这些成员函数去访问其基类中自带的"private"、"protected"和"public"类型的成员变量和成员函数。之后再在main函数中创建一个“rectangle”对象r1,再尝试用它可以调用哪些成员函数和访问哪些成员变量。

    下面是测试代码。由于时间关系,这里的代码并没有调用<iostream>库文件的控制台显示的功能,只是通过编译器Visual Studio2017的编译来测试对类内各种成员是否可以访问(直接或间接)。有兴趣的话可以在里面的各个成员函数中添加"cout << "来辅助观察效果。

//本程序可直接粘贴在.cpp文件中运行,
//仅用于了解private和public在类继承中如何对类成员赋予权限,
//编译可以通过即可,运行也不会在屏幕上显示任何信息。
//主要的讲解都在注释当中。
#include <iostream>
using namespace std;

//定义一个基类,名为“图形”
class shape			
{
//基类私有成员(包括私有成员变量和私有成员函数)
private:			
	//基类私有成员变量
	double x;		//横坐标
	double y;		//纵坐标
	double scale;	//尺度
	//基类私有成员函数
	void reset()	//重置横纵坐标和尺度坐标
	{
		x = y = 0.0;
		scale = 1.0;
	}
//基类保护成员(包括保护成员变量和保护成员函数)
protected:
	//基类保护成员变量
	int color;				//颜色
	//基类保护成员函数
	void show_color(){}		//显示颜色(空函数)
//基类公有成员(均为成员函数)
public:				
	shape(double _x, double _y, double _sacle,int _color)		//构造函数
		:x(_x), y(_y), scale(_sacle),color(_color) {}
	virtual ~shape() {}								//析构函数
	void move_x(double _x)		{ x = _x; }			//移动横坐标
	void move_y(double _y)		{ y = _y; }			//移动纵坐标
	void resize(double _scale) { scale = _scale; };	//放缩尺度
	void normalize() { reset(); }					//标准化
	void call_show_color(){ show_color(); }			//仅用来调用基类保护成员函数
	void access_color() { color = 255; }			//仅用来访问基类保护成员变量
};

//从“图形”基类生成一个派生类,名为“长方形”
class rectangule
	:public shape	//继承“图形”基类
{
//派生类私有成员
private:			
	//派生类私有成员变量
	double angle;	//长方形朝向角度
	double ratio;	//长方形的长宽比
	//派生类私有成员函数
	void set_ratio(double _ratio) { ratio = _ratio; }	//设置长宽比
//派生类上共有成员(均为成员函数)
public:				
	rectangule									//构造函数
	(double _x,double _y,double _scale,double _angle,int _color)
		:shape(_x,_y,_scale,_color),angle(_angle),ratio(1.0){}
	virtual ~rectangule(){}						//析构函数
	void set_angle(double _angle)				//设置长方形朝向角度
	{
		angle = _angle;
	}
	void set_square()	{ set_ratio(1.0); }		//设置为正方形
	void set_long()		{ set_ratio(2.0); }		//设置为2:1长方形
	//以下操作为示例操作,在此仅为说明继承中基类的private访问权限。
	//void amplify1(){ scale = 10.0;}			//将长方形尺度改为10
	//由于派生类的public成员函数不可访问scale,失效
	void amplify2() { resize(10.0); }			//将长方形尺度改为10
	//void reset_rect1() { reset(); }			//重置长方形
	//由于派生类的public成员函数不可访问reset(),失效
	void reset_rect2() { normalize(); }			//重置长方形
	//可见,派生类在基类之上新增加的成员函数,只能直接访问其新增加的
	//私有成员函数和私有成员变量,而无法访问其从基类继承来的私有成员
	//变量和私有成员函数。
	/*此处着重强调一下*/
	//很多教材或者博客常用的说法都是“派生类只能继承基类的public部分,
	//而无法继承其private部分”。其实这样的说法过于笼统和模糊,容易对
	//初学者造成误导。
	//更恰当的说法应该是“派生类继承了基类所有的内容,但它在此基础上
	//新增加的成员函数无权直接访问它从基类继承来的private部分,它只能
	//通过它所继承的基类的public成员函数来间接访问它从基类继承来的
	//private成员变量和成员函数。

	//为派生类增加public成员函数,试图直接访问它从基类继承来的	
    //protected的成员变量和成员函数。结果发现,在派生类中新增加的public
	//成员函数确实有直接访问基类中protected成员变量和成员函数的权限。
	void d_call_show_color()	{ show_color(); }	//
	void d_access_color()		{ color = 244; }
	
};

//主函数,测试两种类对象对其内部各变量的操作权限
int main()
{
//1. 测试“图形”对象以说明
//一个普通基类对象对其private成员、protected成员和public成员的访问权限。
//类对象s1仅可直接访问以下属于public的成员函数。
	
	shape s1(10, 20, 1.0,127);	
	s1.move_x(15);
	s1.move_y(25);
	s1.normalize();
	s1.resize(2.0);
	s1.~shape();
	s1.access_color();
	s1.call_show_color();

	//s1.x = 22;	//成员变量x位于private当中,类对象s1无法直接访问它。
	//但s1.move_x(15)这个类对象的public成员函数却可以访问x。
	//s1.reset();	//成员函数reset()位于private当中,类对象s1无法直接访问它。
	//但s1.normalize()这个类对象的public成员函数却可以访问reset()。
	//s1.color = 255;	//成员变量color位于protected当中,类对象s1无法直接访问它。
	//但s1.access_color()这个类对象的public成员函数却可以访问color。
	//s1.show_color();	//成员函数show_color()位于protected当中,
	//类对象s1无法直接访问它。
	//但s1.call_show_color()这个类对象的public成员函数却可以访问show_color()。

	//综上,类对象不可以直接访问private的成员变量和成员函数,
	//只能通过类内部的其他成员函数访问它们,
	//这就是封装应有的效果。
	
//2. 测试“长方形”对象以说明
//一个派生类对象对其所继承的基类的private成员、protected成员和public成员,以及
//其新增添的private成员与public成员的访问权限。
//类对象r1仅可以直接访问以下属于public的成员函数。

	rectangule r1(11, 21, 1.0, 5.0,127);
	//所继承的基类自带的public成员函数
	r1.move_x(1.0);
	r1.move_y(1.2);
	r1.normalize();
	r1.resize(2.0);

	//派生过程中新添加的public成员函数
	r1.amplify2();
	r1.reset_rect2();
	r1.set_angle(5.1);
	r1.set_long();
	r1.set_square();
	r1.~rectangule();
	r1.d_call_show_color();
	r1.d_access_color();

	system("pause");
	return 0;
}

    最后,我总结了一张图来表示public继承中的private、protected和public成员之间的访问关系。

    以上就是我目前学习到的public继承中所涉及的知识。如果图中有错误,请留言告诉我,谢谢!

    至于protected成员的具体用法,我还不是很清楚,会在后面的博客中继续记录下去。

   

猜你喜欢

转载自blog.csdn.net/yibeiyese/article/details/82794548