C++学习笔记:(六)public、protected、private继承详解_float geth() const{return h}有什么用_Tyler_Zx的博客-程序员宅基地

技术标签: C/C++  C 继承详解  

前言

上一篇学习了继承的基础概念以及示例代码。算是对继承有了一个简单的了解。如果想要对继承有更深的了解,就要复习访问权限的知识点。这样才能深化对继承的了解,以及学习不同的继承方式能对哪些数据进行操作。

类成员包括数据成员和函数成员,分别描述问题的属性和行为,是不可分割的两个方面。对类成员访问权限的控制,是通过设置成员的访问控制属性来实现的。访问控制属性有:公有类型、私有类型和保护类型。这里简单的说一下不同类型的作用。公有类型成员定义了类的接口。私有成员只能被本类的成员函数和友元函数访问,来自类外部的任何访问都是非法的。这样,私有成员就完全隐藏在类中,保护了数据的安全性。保护类型成员的性质和私有成员的性质相似,其差别在于继承过程中对派生类影响不同。

类中public、protected和private数据成员、函数成员的使用:

#include <iostream>
using namespace std;

class Asd
{
	public:
		int x;
		Asd(int a = 0, int b = 0, int c = 0);
		int getx(){return x;}
		int gety(){return y;}
                void sety1(int b){y = b;}
		int getz(){return z;}
                void setz1(int c){z = c;}
		void sety2(int b){protectedsety(b);}
		void setz2(int c){privatesetz(c);}
		void print()
		{
			cout << "X:" << x << " " << "Y:" << y << " " << "Z:" << z <<endl;
		}
	protected:
		int y;
		void protectedsety(int b){y = b;}
	private:
		void privatesetz(int c){z = c;}
		int z;
};

Asd::Asd(int a, int b, int c)
{
	x = a;
	y = b;
	z = c;
}

int main()
{
	Asd a(3, 2, 3);
        a.print();                  //通过print()函数打印
        a.sety1(3);                 //通过public中的函数设置y
        a.setz1(4);                 //通过public中的函数设置z
        a.print();
        a.sety2(4);                 //由protected中的函数设置y
        a.setz2(5);                 //由private中的函数设置z
        cout << "(x,y,z):";
        cout << "(" << a.getx() << "," << a.gety() << "," << a.getz() << ")" <<endl;  //由public中的函数得到数据
        cout << "X:" << a.x <<endl;   //正确,对象可以直接访问公有数据
        //cout << "Y:" << a.y <<endl; //错误,对象不能直接访问保护数据
        //cout << "Z:" << a.z <<endl; //错误,对象不能直接访问私有数据
        //a.protectedsety(1);         //错误,对象不能直接访问保护成员函数
        //a.privateserz(1);           //错误,对象不能直接访问私有成员函数
	return 0;
}

在这个示例代码中,每种类型都有数据成员和函数成员。可以清楚的看到public中的函数是如何充当接口的。在这个类中,可以通过public中的函数直接设置y、z的值,也可以通过public中的函数去访问protected和private中的函数,最终达到设置y、z值的目的。在public中的数据成员可以通过”对象名.数据成员”的方式直接使用,而其他类型的数据成员则不行。

 

接下来开始分析不同的继承方式:

公有继承:

#include <iostream>
using namespace std;

class Point
{
	private:
		float x;
		float y;
                float findx()const{return x;}
	protected:
                void setz(int a){z = a;}
		float z;
	public:
		void initPoint(float x = 0, float y = 0, float z = 0)
		{
			this->x = x;
			this->y = y;
			this->z = z;
		}
		Point(){}
		Point(float r)
		{ 
			x = r;
		}
		void move(float a, float b)
		{
			x += a;
			y += b;
		}
		float getx()const{return x;}
		float gety()const{return y;}
                float getz()const{return z;}
		float getprivatex()const{return findx();}
};

class Rectangle:public Point
{
	private:
		float w,h;
	public:
		void initRectangle(float x, float y, float w, float h)
		{
			initPoint(x,y);
			this->w = w;
			this->h = h;
		}
		float geth()const{return h;}
		float getw()const{return w;}
                //float publicgetx1()const{return getx();}            //正确,getx()是基类公有成员函数
                //float publicgetx2()const{return findx();}           //错误,findx()是基类中的私有成员函数
                float publicgetx3()const{return getprivatex();}       //正确,getprivatex()是基类公有成员函数
                void publicsetz(int a){setz(a);}                      //正确
};

int main()
{
	Rectangle rect;
	rect.initRectangle(2,3,10,10);
        rect.move(3,2);
	cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;
        cout << "X:" << rect.publicgetx3() <<endl;
        rect.publicsetz(20);
        cout << "Z:" << rect.getz() <<endl;
        //rect.setz(1);       //错误,派生类对象不能直接访问基类保护成员函数
        //rect.findx();       //错误,派生类对象不能直接访问基类私有成员函数
	return 0;
}

(1)基类的private、public、protected成员的访问属性在派生类中保持不变。

(2)派生类中继承的成员函数可以直接访问基类中所有成员派生类中新增的成员函数只能访问基类的public和protected成员,不能访问基类的private成员。

在上述代码中,基类中的move(),getx(),gety()都被派生类继承了,而且move(),getx()&gety()在基类中就是public中的函数。由(1)可知,派生类对象可以直接使用这些函数。由派生类中新增的三个publicgetx()函数可知,派生类新增函数能访问public成员,不能访问private成员。由publicsetz()可知,派生类新增函数能访问protected成员。若想让派生类中的新增成员函数访问private成员,可以通过基类中public中的函数间接的访问基类的private成员(如:publicgetx3()函数)。可以这样做,但一般不会这样做,因为在做项目的时候是不会动基类的。

(3)通过派生类的对象只能访问基类的public成员。(rect.setz(1)和rect.findx()报错的原因是它们不是基类public的成员函数)

 

保护继承:

#include <iostream>
using namespace std;

class Point
{
	private:
		float x;
		float y;
		float findx()const{return x;}
	protected:
		void setz(int a){z = a;}
		float z;
	public:
		void initPoint(float x = 0, float y = 0, float z = 0)
		{
			this->x = x;
			this->y = y;
			this->z = z;
		}
		Point(){}
		Point(float r)
		{ 
			x = r;
		}
		void move(float a, float b)
		{
			x += a;
			y += b;
		}
		float getx()const{return x;}
		float gety()const{return y;}
		float getz()const{return z;}
		float getprivatex()const{return findx();}
};

class Rectangle:protected Point
{
	private:
		float w,h;
	public:
		void initRectangle(float x, float y, float w, float h)
		{
			initPoint(x,y);
			//setz(10);               //派生类可以访问基类保护成员函数
			this->w = w;
			this->h = h;
		}
		void move(float a, float b){Point::move(a,b);}       //与public继承的不同
		float getx()const{return Point::getx();}             //与public继承的不同
		float gety()const{return Point::gety();}             //与public继承的不同
		float getz()const{return Point::getz();}             //与public继承的不同
		float geth()const{return h;}
		float getw()const{return w;}
		//float publicgetx1()const{return getx();}            //正确,getx()是基类公有成员函数
		//float publicgetx2()const{return findx();}           //错误,findx()是基类中的私有成员函数
		float publicgetx3()const{return getprivatex();}       //正确,getprivatex()是基类公有成员函数
		void publicsetz(int a){setz(a);}                      //正确
};


int main()
{
	Rectangle rect;
	rect.initRectangle(2,3,10,10);
	rect.move(3,2);
	cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;
	cout << "X:" << rect.publicgetx3() <<endl;
	rect.publicsetz(20);
	cout << "Z:" << rect.getz() <<endl;
	//rect.setz(1);       //错误,派生类对象不能直接访问基类保护成员函数
	//rect.findx();       //错误,派生类对象不能直接访问基类私有成员函数
	return 0;
}

(1)基类的public、protected成员都以protected身份出现在派生类中。

(2)派生类中新增的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。

由(1)可知,基类的public成员都以protected身份出现在派生类中,所以派生类的对象不能像公有继承那样直接使用基类中public的函数。而是要根据需求添加成员函数。例如:

void initRectangle(float x, float y, float w, float h)
{
    initPoint(x,y);
    this->w = w;
    this->h = h;
}
void move(float a, float b){Point::move(a,b);}       //与public继承的不同
float getx()const{return Point::getx();}             //与public继承的不同
float gety()const{return Point::gety();}             //与public继承的不同
float getz()const{return Point::getz();}             //与public继承的不同

因为派生类中新增的成员函数可以直接访问基类中的public和protected成员,而且在派生类中没有initPoint()的同名函数,所以在initRectangle()函数中可以直接使用initPoint()函数,而不需要写成Point::initPoint();。如果派生类对象要使用getx()函数,那么就要在派生类public中定义:

方法一:指明getx()的来历
float getx()const{return Point::getx();}。
方法二:为了避免重名可以写成:
float protectedgetx()const{return getx();}。

(3)通过派生类的对象不能访问基类的任何成员。(因为继承来的成员以protected身份出现在派生类中)

 

私有继承:

#include <iostream>
using namespace std;

class Point
{
	private:
		float x;
		float y;
		float findx()const{return x;}
	protected:
		void setz(int a){z = a;}
		float z;
	public:
		void initPoint(float x = 0, float y = 0, float z = 0)
		{
			this->x = x;
			this->y = y;
			this->z = z;
		}
		Point(){}
		Point(float r)
		{ 
			x = r;
		}
		void move(float a, float b)
		{
			x += a;
			y += b;
		}
		float getx()const{return x;}
		float gety()const{return y;}
		float getz()const{return z;}
		float getprivatex()const{return findx();}
};

class Rectangle:private Point
{
	private:
		float w,h;
	public:
		void initRectangle(float x, float y, float w, float h)
		{
			initPoint(x,y);
			this->w = w;
			this->h = h;
		}
		void move(float a, float b){Point::move(a,b);}       //与public继承的不同
		float getx()const{return Point::getx();}             //与public继承的不同
		float gety()const{return Point::gety();}             //与public继承的不同
		float getz()const{return Point::getz();}             //与public继承的不同
		float geth()const{return h;}
		float getw()const{return w;}
		//float publicgetx1()const{return getx();}            //正确,getx()是基类公有成员函数
		//float publicgetx2()const{return findx();}           //错误,findx()是基类中的私有成员函数
		float publicgetx3()const{return getprivatex();}       //正确,getprivatex()是基类公有成员函数
		void publicsetz(int a){setz(a);}
};

int main()
{
	Rectangle rect;
	rect.initRectangle(2,3,10,10);
	rect.move(3,2);
	cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;
	cout << "X:" << rect.publicgetx3() <<endl;
	rect.publicsetz(20);
	cout << "Z:" << rect.getz() <<endl;
	//rect.setz(1);       //错误,派生类对象不能直接访问基类保护成员函数
	//rect.findx();       //错误,派生类对象不能直接访问基类私有成员函数
	return 0;
}

(1)基类的public、protected成员都以private身份出现在派生类中。(与保护继承的不同)

(2)派生类中新增的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。(这里和保护继承的情况是一样的)

(3)通过派生类的对象不能访问基类的任何成员。(因为继承来的成员以private身份出现在派生类中)。可以和上述保护继承一样,通过新增成员函数让对象使用基类的一些操作。

数据成员和函数成员在继承过程中以相应继承方式出现在派生类中的情况可以这样理解:

注意:private和protected之间的区别只有在基类派生的类中才能表现出来。派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。因此,对外而言,保护成员的行为与私有成员相似;对内而言,保护成员的行为与公有成员相似。

 

私有继承与保护继承的区别:

在上述的代码中,私有继承和保护继承的区别可能不是很明显。要想了解的更深,我们可以在Rectangle类的基础上再派生新的类,在新的派生类中看不同的继承方式对类中各成员的影响。

保护继承后再公有继承:

#include <iostream>
using namespace std;

class Point
{
	private:
		float x;
		float y;
		float findx()const{return x;}
	protected:
		void setz(int a){z = a;}
		float z;
	public:
		void initPoint(float x = 0, float y = 0, float z = 0)
		{
			this->x = x;
			this->y = y;
			this->z = z;
		}
		Point(){}
		Point(float r)
		{ 
			x = r;
		}
		void move(float a, float b)
		{
			x += a;
			y += b;
		}
		float getx()const{return x;}
		float gety()const{return y;}
		float getz()const{return z;}
		float getprivatex()const{return findx();}
};

class Rectangle:protected Point
{
	private:
		float w,h;
	public:
		void initRectangle(float x, float y, float w, float h)
		{
			initPoint(x,y);
			//setz(10);               //派生类可以访问保护成员函数
			this->w = w;
			this->h = h;
		}
		void move(float a, float b){Point::move(a,b);}       //与public继承的不同
		float getx()const{return Point::getx();}             //与public继承的不同
		float gety()const{return Point::gety();}             //与public继承的不同
		float getz()const{return Point::getz();}             //与public继承的不同
		float geth()const{return h;}
		float getw()const{return w;}
		//float publicgetx1()const{return getx();}            //正确,getx()是基类公有成员函数
		//float publicgetx2()const{return findx();}           //错误,findx()是基类中的私有成员函数
		float publicgetx3()const{return getprivatex();}       //正确,getprivatex()是基类公有成员函数
		void publicsetz(int a){setz(a);}
};

class A:public Rectangle
{
	private:
		float a;
	public:
		void initA(float x)
		{
			initRectangle(1,2,3,4);
			a = x;
                        setz(20);                                      //正确,setz()是Point类的protected成员              
		}
		//float getx()const{return Rectangle::getx();}         //正确  
                float getx()const{return Point::getx();}               //正确
};


int main()
{
	Rectangle rect;
	rect.initRectangle(2,3,10,10);
	rect.move(3,2);
	cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;
	cout << "X:" << rect.publicgetx3() <<endl;
	rect.publicsetz(20);
	cout << "Z:" << rect.getz() <<endl;
	//rect.setz(1);       //错误,派生类对象不能直接访问基类保护成员函数
	//rect.findx();       //错误,派生类对象不能直接访问基类私有成员函数
	A a;
        a.initA(10);
	cout << "A.getx():" << a.getx() <<endl;
	return 0;
}

因为Rectangle类是保护继承,Point类的public、protected成员都以protected身份出现在派生类中,而A是公有继承的(基类各属性保持不变),所以在A类中新增成员函数可以使用setz()函数。而且在定义A类中getx()函数时,以下两种形式都是正确的。(改变class A的继承方式,上面的程序都能正常运行)原因是保护继承后再公有继承,A类仍然可以访问到Point类的成员。

float getx()const{return Rectangle::getx();}       
float getx()const{return Point::getx();}

私有继承后再公有继承:

#include <iostream>
using namespace std;

class Point
{
	private:
		float x;
		float y;
		float findx()const{return x;}
	protected:
		void setz(int a){z = a;}
		float z;
	public:
		void initPoint(float x = 0, float y = 0, float z = 0)
		{
			this->x = x;
			this->y = y;
			this->z = z;
		}
		Point(){}
		Point(float r)
		{ 
			x = r;
		}
		void move(float a, float b)
		{
			x += a;
			y += b;
		}
		float getx()const{return x;}
		float gety()const{return y;}
		float getz()const{return z;}
		float getprivatex()const{return findx();}
};

class Rectangle:private Point
{
	private:
		float w,h;
	public:
		void initRectangle(float x, float y, float w, float h)
		{
			initPoint(x,y);
			this->w = w;
			this->h = h;
		}
		void move(float a, float b){Point::move(a,b);}       //与public继承的不同
		float getx()const{return Point::getx();}             //与public继承的不同
		float gety()const{return Point::gety();}             //与public继承的不同
		float getz()const{return Point::getz();}             //与public继承的不同
		float geth()const{return h;}
		float getw()const{return w;}
		//float publicgetx1()const{return getx();}            //正确,getx()是基类公有成员函数
		//float publicgetx2()const{return findx();}           //错误,findx()是基类中的私有成员函数
		float publicgetx3()const{return getprivatex();}       //正确,getprivatex()是基类公有成员函数
		void publicsetz(int a){setz(a);}
};

class A:public Rectangle
{
	private:
		float a;
	public:
		void initA(float x)
		{
			initRectangle(1,2,3,4);
			a = x;
                        //setz(20);                                    //报错,因为setz()是Point的保护成员
		}
                float getx()const{return Rectangle::getx();}           //正确
                //float getx()const{return Point::getx();}             //错误,不能访问Point类的getx()
};

int main()
{
	Rectangle rect;
	rect.initRectangle(2,3,10,10);
	rect.move(3,2);
	cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;
	cout << "X:" << rect.publicgetx3() <<endl;
	rect.publicsetz(20);
	cout << "Z:" << rect.getz() <<endl;
	//rect.setz(1);       //错误,派生类对象不能直接访问基类保护成员函数
	//rect.findx();       //错误,派生类对象不能直接访问基类私有成员函数
        A a;
        a.initA(10);
        cout << "A.getx():" << a.getx() <<endl;
	return 0;
}

上述代码可以这样理解:

结合上面两个代码和图片可以看出私有继承与保护继承的区别。①因为Rectangle私有继承后,基类的public、protected成员都以private身份出现在派生类中。而setz()函数是Point类中的保护成员(相当于第三种情况),在Rectangle中是私有成员,所以A类中不能访问setz()函数(A的新增成员函数只能访问私有继承的Rectangle中的A2,B2,但是setz()相当于是B1)。②Rectangle类中的getx()函数在public中,A类中的新增成员函数能访问到(Rectangle的getx()相当于是A2),所以float getx()const{return Rectangle::getx();}是正确的。由于Rectangle私有继承Point,所以A访问不到Point类的getx()函数(Point类的getx()相当于是A1),所以float getx()const{return Point::getx();}是错误的,换句话说就是私有继承后再公有继承的派生类访问不到上上个基类的成员。以上两点就是公有继承与私有继承的区别。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38289815/article/details/81637900

智能推荐

c语言哈夫曼编码 贪心,[C++]哈夫曼树(最优满二叉树) / 哈夫曼编码(贪心算法)-程序员宅基地

一 哈夫曼树1.1 基本概念算法思想贪心算法(以局部最优,谋求全局最优)适用范围1 【(约束)可行】:它必须满足问题的约束2 【局部最优】它是当前步骤中所有可行选择中最佳的局部选择3 【不可取消】选择一旦做出,在算法的后面步骤中,就无法再改变。示例【树论:最优(二叉)数=带权路径最短的树】【图论:单源最短路径=从某一结点出发至其他结点的带权路径之和最小 / 多源最短路径=某一对结点之间的带权路径之..._哈夫曼编码贪心算法c语言

2017/7/19 学习心得 html5-程序员宅基地

1、多媒体1)音频autoplay:自动播放controls:控制条loop:循环播放兼容写法:支持谁选择谁2)视频兼容性写法同上

多线程的那点儿事(之原子锁)_vxworks 原子锁_嵌入式-老费的博客-程序员宅基地

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 原子锁是多线程编程中的一个特色。然而,在平时的软件编写中,原子锁的使用并不是很多。这其中原因很多,我想主要有两个方面。第一,关于原子锁这方面的内容介绍的比较少;第二,人们在编程上面习惯于已有的方案,如果没有特别的需求,不过贸然修改已存在的代码。毕竟对很多人来说,不求有功,但求无过_vxworks 原子锁

vue 探讨同级组件间值的传递($on,$emit的综合运用)_同级页面.$emit 传值-程序员宅基地

想法:最近在做一个数据处理分析的系统,页面比较多,一些共用的功能也比较多,于是想起了组件化开发,想着一个组件封装好了就可以在项目的各个地方直接引入,无需再次重复编写。举例:头部的导航栏抽出,登录模块抽出,登录按钮存放在头部导航栏里,点击按钮的时候显示/隐藏登录模块,(因登录模块在后台页面里的某个地方将再次需要,只是调起的方式不一样)。一开始登录模块默认隐藏状态,点击登录按钮时设置一个变量存放点击的..._同级页面.$emit 传值

python numpy数组转置和矩阵乘法_numpy行向量转置-程序员宅基地

文章目录一.转置1.使用 array.T:2.使用array.transpose()方法二.矩阵乘法1.使用运算符 *2.使用numpy.multiply()方法3.使用numpy.dot()方法三.一维数组的特例一.转置数组转置1.使用 array.T:1.对于array对象,若是一维数组(行向量),array.T并不会把行向量转化成列向量,见下:import numpy as..._numpy行向量转置

ERROR: Could not build wheels for pycocotools which use PEP 517 and cannot be installed directly_机械专业的计算机小白的博客-程序员宅基地

ERROR: Command errored out with exit status 1: command: 'D:\anaconda\envs\pytorch\python.exe' 'D:\anaconda\envs\pytorch\lib\site-packages\pip\_vendor\pep517\in_process\_in_process.py' build_wheel 'C:\Users\gh\AppData\Local\Temp\tmpsnf99kcw' c..._error: could not build wheels for pycocotools which use pep 517 and cannot b

随便推点

jw player flash网页播放器 参数说明以及应用配置-程序员宅基地

jw player flash网页播放器 参数说明以及应用配置1、参数解释这些参数可以配置被嵌入到html中的播放器的行为和外观。如果使用swfobject.js,可以用addVariable()方法来配置,如果使用的embed方式,可以通过定义"flashvars"属性来配置变量,注意用"&"符号分割这些变量。被*号标注的同样适用于 J...

VM虚拟机添加用户组和用户名时报错:sudo提示用户不在sudoers文件中,sudo groupadd -g 5400 Tester[sudo] password for ty:ty 不在 文件中。_sudo组不存在-程序员宅基地

操作系统一、VM虚拟机添加用户组和用户名时报错: sudo提示用户不在sudoers文件中的解决方法添加一个新的用户组Tester时,无法正常添加,提示:sudo groupadd -g 5400 Tester[sudo] password for ty:ty 不在 sudoers 文件中。1、切换到root用户[ty@ty ~]$su root[ty@ty ~]$su root2、查看/etc/sudoers文件权限,将只读权限,修改为可写权限[root@ty..._sudo组不存在

HTTP Status 500 - Servlet.init() for servlet DispatcherServlet threw exception-程序员宅基地

车祸现场一type Exception reportmessage Servlet.init() for servlet DispatcherServlet threw exceptiondescription The server encountered an internal error that prevented it from fulfilling this request....

vue 源码 关于 props methods data computed watch 的执行顺序_watch和props触发的先后-程序员宅基地

//vue 源码 关于 props methods data computed watch 的执行顺序function initState (vm) { vm._watchers = []; var opts = vm.$options; if (opts.props) { initProps(vm, opts.props); } if (opts.methods) { initMethods(vm, opts.methods); } _watch和props触发的先后

数据分析之异常值分析-箱线图-程序员宅基地

箱线图大于上界(上四分位数,Qu+1.5IQR)小于下界(下四分位数,Ql-1.5IQR)都是异常值,IQR是上四分位数据Qu和下四分位数据Ql的差距,包含一半数据,具有一定的鲁棒性,不受异常值的影响转载于:https://www.cnblogs.com/hanshuai0921/p/9067026.html..._qu + m * iqr

不知道什么时候会鸽出来的blog-程序员宅基地

线段树的全方面拓展主要涉及zkw线段树,线段树动态开点,线段树合并,李超线段树,吉司机线段树,二维线段树,四叉树,线段树分治,线段树优化建图.分块的入门主要涉及序列分块,树分块,以及一种神奇的对操作序列分块的方法.莫队的入门主要涉及序列莫队,回滚莫队,带修莫队,二维莫队以及树上莫队.替罪羊树fhq treap及其可持久化树分治主要涉及点分治,边分治,动态点分治.全局平衡二叉树...