C++:继承、派生与复合——概念与实战

基本概念

继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指B拥有A的全部特点),就可以将A作为一个基类,将B作为基类的一个派生类(也即子类)。

关于派生类,是通过对积累进行修改和补充得到的

在派生类中,不仅拥有基类的全部成员函数和成员变量,不论private、protected、public,虽然派生类的各个成员函数,均不能访问在积累中的private成员
派生类也还可以扩充新的成员变量和成员函数。

派生类对象的内存空间

关于派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。
在派生类对象中,包含着积累对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。

以下是关于其内存空间的一个实例:
在这里插入图片描述


继承实例的程序——学籍管理

#include <iostream>
#include <string>
using namespace std;

class CStudent
{// 基类
	private:
		string name;
		string id;
		char gender;//性别
		int age;
	public:
		void PrintInfo();//输出个人信息
		void SetInfo( const string & name_, const string & id_, 
			int age_, char gender_);//读入信息
		string GetName(){ return name;}
};

class CUndergraduateStudent:public CStudent
{// 本科生,继承自CStudent
	private:
		string department;//学生专业
	public:
		void QualifiedForBAOyan(){
			cout<< "qualified for baoyan"<<endl;
		}
		void PrintInfo(){
			CStudent::PrintInfo();// 首先调用基类的PrintInfo()
			cout << "Department:"<<department <<endl;// 完成派生类中独有的操作
		}
		void SetInfo( const string & name_, const string & id_,
			int age_, char gender_, const string &department_)
		{
			CStudent::SetInfo(name_,id_,age_,gender_);//调用基类的SetInfo
			department = department_;
		}
};

void CStudent::PrintInfo()
{
	cout<< "Name:" << name<<endl;
	cout<< "ID:" << id <<endl;
	cout<< "Age:"<<age << endl;
	cout <<"Gender:" << gender <<endl;
}
void CStudent::SetInfo(const string & name_, const string & id_, int age_,char gender_)
{
	name = name_;
	id = id_;
	age = age_;
	gender = gender_;
}

int main()
{
	CUndergraduateStudent S2;
	s2.SetInfo("Harry Peter","118829212",19,'M',"Computer Science");
	cout << s2.GetName()<<"";//输出姓名后不换行
	s2.QualifiedForBaoyan();
	s2.PrintInfo();
	system("pause");
	return 0;
}
/*输出
Harry Potter qualified for baoyan
Name:Harry Potter
ID:118829212
Age:19
Gender:M
Department:Computer Science
*/

继承和复合的区别

关于继承,简单来说是“是”的关系
关于复合,简单来讲是“有”的关系

先不用纠结上面两句话的意思,往下看

( i )在继承中,有类B是基类A的派生类。
即class B: public A { }
则我们在逻辑上要求,一个B对象,首先也要是一个A对象
( i i )在复合中,有类C和类D是复合关系
即,类C中有一个成员变量k,k是类D的一个对象,则C与D是复合关系
我们在逻辑上要求,D对象是C对象的固有属性或组成部分

这样的话,我们再看本节开头那两句话

关于继承,简单来说是“是”的关系
关于复合,简单来讲是“有”的关系

是不是还挺有道理的~

复合关系的两个例子

1 点与圆

在集合形体程序里,我们写出“点”类和“圆”类,易犯的一个错误是什么呢,将“圆”看做“点”的一个派生。像下面这样写:

class CPoint
{
	double x,y;
};

class CCircle:public CPoint
{
	double r;//半径
	//将CPoint中x,y视为圆心坐标
};

这样对么?当然不对

首先,派生类是不能访问基类的私有成员的,对叭?这里“圆”显然不能使用类“点”中的两个变量。其次,在逻辑上,圆就不是一个点,对叭?

那么我们说,这是什么关系呢?圆和点之间显然是复合关系:
每个“圆”对象中都包含有一个“点”对象,即圆心。
那我们就可以这么写:

class CPoint
{//原写法基础上加上友元类CCircle
	double x,y;
	friend class CCircle;
};
class CCircle
{
	double r;//半径
	CPoint center;//圆心
};

这样可以吧~很善了哈哈,CCircle作为友元类的操作可以使之能够对圆心进行操作,当然我们也可以讲浮点数x,y声明为public

2 狗与人

要写一个小区养狗的管理程序,需要一个“业主”类和一个“狗”类。假设狗只有一户主人,但一个业主最多可以有十条狗。

最直接的,我们写出如下代码段

//error 1
class CDog;
class CMaster
{
	CDog dogs[10];
};
class CDog
{
	CMaster m;
};

这样的话,显然是错误的。
为什么错呢,CMaster 的定义中,有10个CDog的对象,而在CDog中,又有一个主人的对象,这样的循环定义,导致这两者均无法算出具体分配的内存空间

//error 2
class CDog;
class CMaster
{
	CDog * pdogs[10];
};
class CDog
{
	CMaster m;
};

我们将狗类设置为业主的成员对象,将业主类中存放着狗类的对象指针数组

这样对吗?对了一半
怎么说呢,这样是可以过编译的,但是每条狗都有自己的一套定义,十条狗内部需要保持主人信息的一致性。但我们上述的定义方式导致了在修改人的信息时,需要给十条狗同时进行修改,这样显然是不够善的。
再来:

//example 3
class CMaster;
class CDog
{
	CMaster * pm;
};
class CMaster
{
	CMaster dogs[10];
};

我们为狗类设置一个业主类的对象指针,为业主类设置一个狗类的对象数组
这样写对了吗?还凑合
能够修正前面指出的两种不足,可以一次性对狗or人做出修改。

值得一提的是,上述代码的编写顺序是固定的,必须先对master进行声明,然后依次写出dog和master的具体函数。这是由于master中的狗狗数组必须能够计算出分配的具体内存空间。

为什么说还不够好呢?因为在这种情况下,作为成员对象,狗是作为人的固有属性存在的,想要对狗进行修改或者读取,是需要通过狗主人而实现的,狗狗失去了“狗格”。(不要嬉皮笑脸哈哈哈)
再写:

// right finally
class CMaster;
class CDog
{
	CMaster * pm;
};
class CMaster
{
	CMaster * dogs[10];
};

我们为狗类设置一个业主类的对象指针,而为业主类设置一个狗类的对象指针数组。
这就非常善了。为啥呢?
人和狗在复合的同时,都具有一定的自主性,不会出现前几个example中各种各样的弊端

类内存放指针对象的含义,或许我们可以理解为“知道”、“了解”的含义,而在类内存放普通对象,可以理解为“拥有”意,成为该类的固有属性,更极端一点说是“附庸”了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖虎干嘛了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值