(一四七)私有继承

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

C++的另一种实现has-a关系的途径(前一种是将类对象作为新类的成员)是私有继承

 

使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。

这意味着基类方法将不会成为派生类对象公有接口的一部分(公有继承,派生类可以使用基类的大部分方法),但可以在派生类的成员函数中使用它们。

 

 

私有继承的特点:

①继承方式类似公有继承,但使用private关键字替代public关键字;

 

②没有显式的对象名;

 

③需要使用其他办法替代 当使用类对象作为类数据成员时使用的方法。

 

④基类被称为子类。

 

 

使用多个基类的继承,被称为 多重继承(MI,通常,MI,尤其是公有MI将导致一些问题,必须使用额外的语法规则来解决他们。

 

 

私有继承的一些特殊之处:

 

①私有继承将提供无名称的子类对象成员,因此不能使用像name来描述对象,而应使用用于公有继承的技术。

 

②访问基类的方法:使用私有继承时,只能在派生类中使用基类的方法,但有时候可能希望基类工具是公有的。那么就在公有方法的函数中,使用私有方法。例如double Student::average(int i) { return arr::average(i); }这样,通过作用域解析运算符来区分。

 

③访问基类的对象:使用强制类型转换,

例如 (const string&)*this,其中this是指向当前对象的指针,*this是当前对象,强制类型转换后获得子类string

对于数组的访问,使用((const arr&)*this)[i],用括号括起来,强制类型转换后(成为一个valarray数组)然后添加运算符。

 

④访问基类的友元函数:

访问类的子类时,使用强制类型转换,将类对象显式转换为基类来调用正确的函数(前提是子类有相应的类方法)

 

 

对普通has-a关系的进行修改,使用私有继承类的方法:

#ifndef STU_
#define STU_
#include<string>
#include<valarray>

using namespace std;
class Student:private string,private valarray<double>
{
	typedef valarray<double> arr;	//将arr作为其别名
	ostream& scores_out(ostream& os)const;	//输出分数,我怀疑可以使用void作为返回值应该也可以。
public:
	Student() :string("No Student"), arr() {}	//默认构造函数,名字为No Student,分数无
	explicit Student(const string& s) :string(s), arr() {}		//构造函数,参数为string类对象,赋值给name,关闭隐式转换
	explicit Student(int n) :string("No name"), arr(n) {}		//构造函数,初始化分数的数量(不是值),关闭隐式转换
	Student(const string&s, int n) :string(s), arr(n) {}		//参数为name赋值,并声明有几个分数
	Student(const string&s, const arr&a) :string(s), arr(a) {}		//参数为name赋值,并将数组赋值给分数(调用的valarray的复制构造函数)
	Student(const string&s, const double*pd, int n) :string(s), arr(pd, n) {}		//类似上面,分数有n个成员,并用pd数组初始化能初始化的那几个
	~Student() {}			//析构函数,不声明也可以
	double Average();		//返回平均分
	const string& Name()const;		//返回姓名
	double & operator[](int i);		//返回第n个分数,可被修改
	double operator[](int i)const;		//这个和上面有啥区别?不能被修改?
										//friend友元函数
	friend istream& operator >> (istream&is, Student& Stu);	//输入,一个单词
	friend istream& getline(istream&is, Student& stu);	//输入,但是一行
	friend ostream& operator<<(ostream&os, const Student&stu);	//输出
};
#endif // !STU_

#include<iostream>
#include"stu.h"
using namespace std;

ostream& Student::scores_out(ostream& os)const
{
	int i = 0;
	int lim = arr::size();		//使用作用域解析运算符替代scores.
	if ( lim > 0)
	{
		for (i = 0;i < lim;i++)
		{
			os << arr::operator[](i) << " ";	//显式调用子类的方法,这里不能使用强制类型转换,因为是数组(
			if (i % 5 == 4)	
				os << endl;
		}
		if (i % 5 != 0)	
			os << endl;
	}
	else
		os << "empty" << endl;
	return os;
}

double Student::Average()	//返回平均分
{
	if (arr::size() > 0)
	{
		return arr::sum() / arr::size();
	}
	else
		return 0;
}

const string& Student::Name()const
{
	return (const string&)*this;	//使用强制类型转换来替代
}
double & Student::operator[](int i)	//返回分数
{
	return arr::operator[](i);	//使用了valarray的类方法
}
double Student::operator[](int i)const
{
	return arr::operator[](i);
}

istream& operator>>(istream& is, Student&stu)//输入,一个单词
{
	is >> (string&)stu;	//因为是输入,不能加const
	return is;
}

istream& getline(istream&is, Student& stu)	//输入,但是一行
{
	getline(is, (string&)stu);
	return is;
}
ostream& operator<<(ostream&os, const Student&stu)	//输出
{
	os << (const string&)stu << "'s scores: " << endl;
	stu.scores_out(os);
	return os;
}

使用包含还是私有继承:

建立has-a关系,一般来说,使用包含,因为包含用起来更简单,也可以包含多个同类对象,例如string name; string firstname; string lastname; 而若使用继承,则只能包含一个子类对象。

但若要使用基类的保护对象,则使用继承,因为只有派生类能访问基类的保护成员,而包含是不能访问被包含对象的基类成员的(只能通过公有接口)。

 

 

保护继承:

保护继承的方式类似私有继承,使用关键字protected

如:

class Student:protected std::string , protected std::valarray<double>

{..}

 

使用保护继承时,基类的公有成员和保护成员,都将成为派生类的保护成员。

 

和私有继承一样,基类的接口在派生类中也是可用的(通过基类对象访问基类的公有方法),

 

但在继承层次之外的结构是不可用的(保护继承是因为成为保护成员,不能在类外访问,即流程为public-->protected,而protected只能被基类和派生类使用)(私有继承则因为本身就视派生类为类外,派生类不能使用私有继承的基类方法,只能通过基类的公共接口调用,使用作用域解析运算符)。

 

因此,二者都可以通过基类接口调用

 

保护继承和私有继承的区别在于:

①在派生类派生出第三代时(即派生类的派生类),由于保护成员将被派生类继承,因此,保护继承的第三代将能使用第一代的基类接口(因为第一代的公共成员在第二代变为保护成员,而保护成员在派生类内,被视为公有成员)。

 

②而私有继承的第三代则不能使用,因为第一代的公共方法在第二代内作为私有方法被继承,因此第二代只能通过第一代的公共接口来调用。

访问形式为:基类的作用域解析运算符来使用方法。但需要注意的是,此时基类已经成为私有成员。

而进入第三代后,由于派生类不能访问基类的私有成员。因此,第三代是不能访问身为第二代私有成员的第一代类对象的(无论第三代通过什么来继承)。

 

附表:



使用using重新定义访问权限:

在保护继承或私有继承时,基类的公有成员将成为派生类的保护或者私有成员(因此,该方法是不能在类外使用的)。如果要使用基类的方法,那么

 

方法一:定义一个该基类方法的派生类方法。

例如:

double Student::sum()const

{
return std::valarray<double>::sum();

}

通过这种方法,Student类调用方法.sum()时,实际上相当于调用基类的sum()方法,即将其基类valarray对象求和。

 

 

方法二:在公有部分使用using声明。

例如:

class Student

{

...

public:

using std::valarray<double>::sum;

...

}

效果和方法一是相同的,即使得基类的sum()方法就像成为了派生类的公有方法一样。

注意:只需要函数名sum,不需要特征标,返回类型,和圆括号。

 

那么对于重载函数的兼容性如何?可以使用所有重载函数?从文中看来是这样的。

 

对于运算符重载,需要加上相应的重载运算符符号。例如:

using std::valarray<double>::operator[];

这将使const和非const版本都可用。

 

需要注意的是:

using声明 只适合继承,不适合 包含。

也就是私有继承可用,但将类对象作为另一个类对象成员时(包含)是不可用的。

 

还有一种老式方式,可用于在派生类中重新声明基类方法,即将方法名放在派生类的公有部分。

如:

class Student

{

...

public:

std::valarray<double>::sum;

...

}

using声明很像,但按书上所说,这种方法即将被抛弃,应放弃使用这种方法,而改用using方法,除非编译器不支持using方法。

 

 

 

 

 


目录
相关文章
|
算法 C# C++
n阶贝塞尔曲线绘制(C/C#)
原文:n阶贝塞尔曲线绘制(C/C#) 贝塞尔是很经典的东西,轮子应该有很多的。求n阶贝塞尔曲线用到了 德卡斯特里奥算法(De Casteljau’s Algorithm) 需要拷贝代码请直接使用本文最后的例程,文章前面的大部分代码都不是最佳实践,是在编程过程中的摸索(走过的弯路),不过这些示范对笔者今后写算法启发很大。
3756 0
|
9月前
|
算法 Linux iOS开发
Zipline 3.0 中文文档(一)(1)
Zipline 3.0 中文文档(一)
573 3
|
人工智能 达摩院 自然语言处理
阿里版ChatGPT「通义千问」邀请测试,我们第一时间试了试
阿里版ChatGPT「通义千问」邀请测试,我们第一时间试了试
1172 2
|
消息中间件 缓存 网络协议
TCP协议的秘密武器:流量控制与拥塞控制
本文将深入探讨TCP协议的关键机制,包括流量控制和拥塞控制,以解密其在网络数据传输中的作用。通过了解TCP协议的工作原理,我们可以更好地理解网络通信的稳定性和可靠性,为我们的网络体验提供更安全、高效的保障。无论您是网络爱好者、技术从业者还是普通用户,本文将为您揭开TCP协议的神秘面纱,带您进入网络传输的奇妙世界。
354 0
TCP协议的秘密武器:流量控制与拥塞控制
|
SQL 关系型数据库 MySQL
【大数据系列之MySQL】(十):使用Navicat运行本地sql文件
【大数据系列之MySQL】(十):使用Navicat运行本地sql文件
477 0
【大数据系列之MySQL】(十):使用Navicat运行本地sql文件
|
人工智能 监控 算法
《多路监控视频实时拼接》原理及案例分享
通过视频拼接技术,对有重叠区域的多路源视频数据利用拼接算法进行无缝实时拼接,消除重叠区域,形成宽角度、大视场视频图像,从而实现将多路监控视频拼接成一路视频,使拼接后的视频清晰无缝,并实时播放,同时支持回放查看,解决多个人同时对同一监控场景不同角度进行观看的需求,充分满足用户的需求。
973 0
|
6月前
|
人工智能 云计算 Anolis
深度探索 AI 与操作系统的融合!阿里云分论坛议程揭晓 | 2024 龙蜥大会
深入探讨 AI 技术与操作系统的融合,推动云计算能力的跃升与应用生态的多元化发展,共同开启智能云时代的新篇章。
|
9月前
|
Shell
ZooKeeper【基础 02】zookeeper-3.6.0 常用Shell命令(节点增删改查+监听器+四字指令)
ZooKeeper【基础 02】zookeeper-3.6.0 常用Shell命令(节点增删改查+监听器+四字指令)
123 0
|
9月前
|
机器学习/深度学习 算法 数据挖掘
使用MICE进行缺失值的填充处理
在我们进行机器学习时,处理缺失数据是非常重要的,因为缺失数据可能会导致分析结果不准确,严重时甚至可能产生偏差。处理缺失数据是保证数据分析准确性和可靠性的重要步骤,有助于确保分析结果的可信度和可解释性。
451 2
|
机器学习/深度学习 编解码 测试技术
如何选择和使用视频质量客观评价指标
“视频质量评定是个大坑”,正如北京大学信息工程学院教授王荣刚所说。
4217 0

热门文章

最新文章