从OOP的角度重看C++(四)——从程序中看OOP

从OOP的角度重看C++(四)——从程序中看OOP

      多重继承(在想要不要把这个放到上一节),就是说一个类的直接父亲不止一个。在现实中的例子就是一个类同时对应好几个类的特征,但是又有自己的特征。在这里,我们举“实习生”的例子:我们有4个类——person,employee,student,empstudent。继承关系如下所示:

        从这里我们可以有两种解释:继承了一次person和继承了两次person。二义性的解决办法:由程序员自己决定

如果想继承一次,那么employee 和 student 在继承person的时候就将person看出虚拟函数;反之,则正常继承两次。

        顺序问题:虚拟函数优先、按继承顺序。上图中,如果employee和student都在printon()中的printwidth进行了修改,那么employee在实例化的时候的值是多少?根据原则,继承的时候按照顺序,那么就应该是最后继承的值。比如: class A : public x1, virtual public x2, public x3, virtual public x4{  },那么继承的顺序就是2,4,1,3;如果修改的话,也是和3的一样。这里还涉及到一个问题,父类中如果有同名函数,那么使用的时候怎么区分呢?是通过作用域符号来看的,比如empstudent的一个实例 es1, es1.employee::printon()和es1.student::printon()表示从不同地方继承来的。

         总结上述,格,引发二义性;防止滥用,用单重继承和组装关系来代替(Java已经不用多重继承了);

         说完继承,我们下面将通过3个经典的程序设计来看OOP的设计思维:分别是从“单链表”看类的分离与关联;同时,从“单链表”的设计看 语言机制的的必要性(friend, private继承);从“标准用户界面操作”看抽象类以及函数指针的使用。

         经典程序设计之 单链表。

         单链表在我们学习数据结构的时候已经知道了;当时是使用结构体来实现的: 首先定义一个node,这个node里面有值和指向后继Node的指针;使用链表的时候只要用一个指向Node的指针就可以了。但是,传统面向过程的方法最大的弊端就是没有体现出封装性,人们可以随意更改每个节点的值。这里我们用OOP的想法。OOP在设计的时候将一个系统(整体,这里就是链表)拆分成若干个可以协同工作的组件,这几个组件共同完成链表的功能。对于链表来说,可以分成3个类: slink 表示单个节点; slist 表示链表类,里面使用成员函数Insert, append和clear 来操作整个链表;slist_interator 作为循环控制机构的载体(原谅我C++基础太差,刚刚才知道啥是迭代器:是一种检查容器内元素并且遍历元素的数据类型。在C++中,定义一个容器,通常会跟着一个该种容器的迭代器,来实现对容器中元素的操作,就像是指针的封装)。

// 节点的定义
typedef void* ent;
class slink{
	friend class slist;
	friend class slist_iterator;
   //slink 类根本没有public成员,用
   //friend规定了外部访问范围
	slink* next;
	ent e;

	slink(ent e, slink* p)
	{
		e=a;
		next=p; 
	}
};

class slist{
	friend class slist_iterator;
	slink* last;
public:
	int insert(ent a);
	int append(ent a);
	void clear();
	slist(){last=0;}  
	slist(ent a)
	{
		last=new slink(a,0);
		last->next=last;
	}
	~slist(){clear()};
};

class slist_iterator
{
slink *ce;
slist *cs;
public:
slist_iterator(slist& s)
{
cs=&s;
ce=0;
}

}
        他们之间的关系如下所示:


      从这个例子可以看出:

尽可能的把类设计单纯,这样将有利于重用;可以看出以上3个类的功能都非常非常简单,简单到,一个节点就是一个节点,连对他的操作也米有!

不同性质/不同层次的数据和操作,要分离到不同的类中,再设计相应的关联结构和协同操作,在这里,这三个类就体现了3种层次

        在单链表着简单的例子中,我们可以看出,语言提供的机制支持肯定是有用 的,比如这个例子就把friend用的很好:slink没有操作,没有public成员,就是因为他的friend可以用啊~~。满足了以上两点,但是我们也会觉得不好用,因为太过于分离,整体感不强。想想我们在初次接受数据结构的时候,那个时候的链表是什么?我们只是定义了一个节点,形成一个“链”也只是使用了他的指针而已!虽然没有封装性,但是大多数时候还是很方便滴~

        如果想要很类似与传统的链表,那么我们可以定义一个“有类型操控和操作投影的外壳”:

struct nlist: private slist{  //<span style="color:#ff0000;">struct 是能见度全部为public的类,这里的private继承是让slist的操作全部不可见~终于也见到有用private继承的了~</span>
	void insert(name * a)
	{
		slist:: insert(a);
	}
	void append(name * a)
	{
		slist::append(a);
	}
	nlist(){}
	nlist(name * a): slist(a){}
};
        这样,我们就可以用着比较顺手了。
        接下来,我们将要看看程序设计的第二个例子:设计用户操作界面( 利用统一接口的方法指针):

// 界面类型(必须是Std_interface的子类)映射表
<span style="white-space:pre">	</span>map<string, Std_interface *> variable;
// 界面操作(必须符合统一的接口规范)映射表
<span style="white-space:pre">	</span>map<string, Pstd_mem> operation; //Pstd_men s; s=& Std_interface::suspend;
/* 将界面操作命令串中的类别和对应的对象指针、界面
操作命令串中的操作和对应的方法指针分别填入上面
两张表中。这一部分是会扩充的,但很单纯。
*/
// 解释执行特定界面类型的特定操作,这里是稳定的。
<span style="white-space:pre">	</span>void call_member(string var, string oper)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">	</span>    (variable[var]->*operation[oper])();
<span style="white-space:pre">	</span>}

        这个例子说明了两个问题:不同的界面;不同的操作。定义的variable和operation分别说明了这两个的区别。看看我们的操作变的懂么简单!!!!本来界面和操作都再变,但是,只要统一他们的接口,在使用的时候就可以快速简单使用! 更重要的:

如果要扩充系统,比如增加新的界面或者新的操作,那么,对系统的扩充从以往的扩充操作据变成了扩充类型!!(扩充操作的问题:扩充操作所带的数据结构的制约,操作代码的大量修改等),前提是要扩充的类型和已有的有比较密切的联系,这是很提倡用继承性机制的做法!

        我个人认为他很棒的另一个原因是:如果每个页面都设计成一个类,里面的操作都不太一样,但是都不太一样,就好比是页面的个数和操作的个数做了个笛卡尔积的感觉~而现在的设计就好比是把一个大表拆成了两个小表!冗余减少,并且要添加新的页面和操作也非常容易!!

        关于面向对象的讨论:对象,类(这可以看成是前几节的概括)

        再次讨论一下什么是对象。对象有:  state, behaviar and identiy。在一个完全面向对象的系统中,一个对象的标识(object Identity)和这个对象永久的结合在一起!他是唯一的,不管这个对象的状态和结构发生了怎样的变化!identify有两个相互正交的属性:表示能力和时态性(目前没有理解,但是仍然把图截下来:)


        类可以有两种理解:一是也对象集合中对象特征的抽象(对象工厂,对象模板),还有是这个集合的整体表示(对象仓库)。类是运行时的概念,但不是用于检查程序是否有错的,而是用于产生和操纵对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值