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

智能推荐

php7+优化加速,加速PHP7,优化PHP7性能-程序员宅基地

PHP 7 比5.x 快上很多,即使只有单纯的版本升级就已经很有感,不过大家还是希望它变得越来越快,这时再做些小调整就可以了。这边主要针对opcache做一些调整。记得启用Zend Opcache, 因为PHP7即使不启用Opcache速度也比PHP-5.6启用了Opcache快, 所以之前测试时期就发生了有人一直没有启用Opcache的事情. 启用Opcache非常简单, 在php.ini配置文..._php7 ini 配置优化

Ceres安装报错“找不到要求版本3.3的Eigen3”解决方法-程序员宅基地

Ceres安装报错“找不到要求版本3.3的Eigen3”解决方法

navicat实现批量插入数据, 或怎么使用函数功能_navicat能使用函数吗-程序员宅基地

首先在navicat中新建函数. 选择过程有需要可以设置下输入或者输出变量然后编写函数, 如实现循环批量插入最后保存, 并运行函数即可_navicat能使用函数吗

微信企业号 技术解析_什么是微信公众企业号的技术实现-程序员宅基地

摘要: 2014年9月18日,微信正式推出企业号公测,以“连接企业的另一种可能”为口号,使微信连接一切的链条更加完整。下面以我个人的角度,对我所理解的企业号,进行简单介绍: 什么是企业号 1.开放平台 企业号提供包 ...2014年9月18日,微信正式推出企业号公测,以“连接企业的另一种可能”为口号,使微信连接一切的链条更加完整。下面以我个人_什么是微信公众企业号的技术实现

Android自定义LinearLayout不执行onDraw方法_android 自定义 linearlayout ondraw没执行-程序员宅基地

设置LinearLayout的背景色可解决不调用问题this.setBackgroundColor(Color.parseColor("#50FFFFFF"));重写下面方法忽略背景色,invalidate();每次都会执行dispatchDraw@Overrideprotected void dispatchDraw(Canvas canvas) { super.dis..._android 自定义 linearlayout ondraw没执行

查找文本中的指定字段-程序员宅基地

package com.test.newfile.Controller;import java.io.*;import java.util.Scanner;/** * @ClassName TextFileSearch * @Author :zhaofuh * @Date :2021/3/15 10:57 * @Description: * @Version: 1.0 */public class TextFileSearch { public void SearchKey.

随便推点

软件测试运维工程师面试题,运维工程师笔试题目-程序员宅基地

这套题目是19年设计,应该是从未使用过,以后也不会在使用,所以将其公开。2 分题(50 分)(1) 列出常见的 Linux 发行版及其包管理工具。(2) 列出常见的容器编排软件。(3) 列出常见的开源存储系统。(4) 列出常见的监控工具。(5) 列出常见的持续集成工具。(6) 常用的远程登录 Linux 服务器的工具有哪些?(7) 你用过或者知道那些虚拟机管理软件?(8) 查看服务器的基本信息:a..._软件运维面试题

前端JS :计时器,定时刷新(执行某方法)_定时器 设置10分钟刷新-程序员宅基地

&lt;!DOCTYPE html&gt;&lt;html&gt; &lt;head&gt; &lt;meta charset="utf-8" /&gt; &lt;title&gt;&lt;/title&gt; &lt;script src="http://libs.baidu.com/jquery/2.0.0/jq_定时器 设置10分钟刷新

angularJS自定义指令各配置项详解_angularjs配置自定义区域设置规则-程序员宅基地

本文对自定义指令中的各个配置项进行了较为详细的说明,部分配置项给出了demo帮助理解。对于初学angularJS 自定义指令这一块的同学可能会有帮助。_angularjs配置自定义区域设置规则

Unity 在WebGL使用WebService_http://192.168.1.190:8006/main.asp-程序员宅基地

继WCF通信在打包安卓调通之后,开始调研WCF在WebGL中是否能够使用。在Unity中切换至WebGL,测试WCF没有任何问题。但是打包之后,报了一个,Reflection.Emit的问题。查找资料发下WebGL不支持Reflection.Emit。最后开始尝试WebService(WebSocket以前用过,是可行的)。1.用Vs2015,C#创建一个ASP.Net空工程。随后发布一个asmx..._http://192.168.1.190:8006/main.asp

@Transactional注解个人总结_translaters注解-程序员宅基地

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Tr..._translaters注解

Froala Editor 在编辑器里上传图片到自己的服务端_froala_editor 图片上传-程序员宅基地

Froala Editor 控件看起来比较简单,在html 可以用div 占位,也可以用textarea 占位 放到textarea 里面,可以直接用form 表单直接提交textare里的数据。下面例子就是这种。备注: Froala Editor 官网地址: https://www.froala.com/wysiwyg-editor/docs/plugins1_froala_editor 图片上传