Boolan C++面向对象高级编程(上)第三周笔记

本文深入探讨面向对象设计中的组合、继承与委托概念,并详细解释模板方法、观察者模式及几种实用的设计模式,如适配器、句柄/主体模式等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.组合和继承

面向对象:类与类之间的关系,分为三种:Inheritance(继承)、Composition(组合)、Delegation(委托)

这三种关系可以单一使用,也可以组合使用形成各种不同的设计模式来处理各种问题。


1.1Composition组合,表示has-a关系

class queue 里面有class deque


上面这张图包含一种设计模式:Adapter(适配)含义如下:

已经有deque,但客户需要queue,而deque的功能完全能满足queue(只需要开放deque的一些接口即可),而queue就是一种Adapter.


1.2composition下的构造与析构 


构造函数和析构函数的调用顺序相反,构造由内而外,析构由外而内。



1.3Delegation(委托).Composition by reference.


  1. 左边has右边(has的是指针),右边是什么,左边其实不知道;左边的class和右边的class用指针相连,它们的生命周期不一致!
    Composition和Delegation虽然都是has-a关系,但还是有区别的。如下:Composition:外部和内部一起出现;Delegation:可能外部的先创建出来,等到需要右边的时候再创建出来,不同步。
  2. 这种写法非常有名:左边的class只是对外的接口,而右边的class来实现。叫做pimpl(pointer implementation)或handle/body(左边是句柄,右边是body)这个指针也可以指向不同的实现类,具有一种弹性,右边怎么变都不影响左边
  3. 有三个字符串都在用同一个hello,三个东西共享一个东西(共享当然很好,节省内存)。但a如果要改变hello不能影响b和c,当要改变内容时,单独拿一份给a来改,b和c来共享原来的东西。但是只有在更改时才给一份副本来写,这就是Copy on write。    

1.4Inheritance(继承),表示is-a


上面的图表示父类的数据是可以完整继承的,但这不是继承最有价值的部分,最有价值的是和虚函数的搭配使用。



1.5Inheritance(继承)关系下的构造和析构


与(2)composition下的构造与析构 的调用顺序基本相同。

注意:base class的dtor必须是virtual,否则会出现undefined behavior !


2.虚函数与多态

继承主要需要搭配虚函数,见如下三种关系:

non-virtual函数:你不希望derived class override(重新定义,重写)它; 
virtual函数:你希望derived class override(重新定义,重写)它,且你对它已有默认定义; 
pure virtual函数:你希望derived class一定要 override(重新定义,重写)它,且你对它没有默认定义;

class Shape
{
  public:
  virtual void draw() const = 0;  //pure virtual
  virtual void error(const std::string& msg); //impure virtual
  int objectID() const; //non-virtual
        ...
};

class Rectangle : public Shape {...};
class Ellipse: public Shape {...};


2.1Template Method( Inheritance with virtual)

这是非常经典的虚函数的写法,也是一种有名的设计模式。(这个Template和C++语法的Template没什么关系)

具体例子如下图


打开一个文件并进行读写,前面的check file name 、search file 、open file都可以事先写好,但是读取的文件类型可能不一样,因此“读”的动作无法事先写好。因此有了下面这样的设计(MFC中的一段代码),如图 


“读”的动作为:Serialize(),设计为虚函数(可能为空函数,也可能为纯虚函数,(空函数和纯虚函数不一样)),CDocument类早已写好,而我们现在需要写CMyDoc类,然后使用。把虚函延缓到子类去决定,可能是一年之后,三年之后在子类去实现,这种用法就叫做Template Method。对于一个做框架的人。应用框架里大量用到Template Method。

具体代码如下



2.2Inheritance+Composition关系下的构造和析构



2.3Observer (Delegation + Inheritance)

 Observer (观察者)设计模式

打开一个文件,用四个窗口打开观察(文件只有一份,却有4个窗口在观察,一个窗口变化,其他窗口的内容也得变化);或者是这样的,一个文件,有三种view在看它(如柱状图,饼状图等)。应该有两个class:一个是表现数据,一个是存储数据。它们之间会有什么关系呢?数据变化,视图都需要跟着变化。


这时候我们就可以采用Observer 设计模式,subject存放数据,subject 有很多的observes,Observe只是个父类,会派生很多不同的Observers。

具体代码如下



因此 Observer 模式的大体结构如下:

  1. subject 中一般会有一个指针容器,通过 observer 来定义增加、删除、通知它旗下的具体观察者。
  2. observer 中会定义一个接口,用于更新数据。
  3. 所有的 instance 通过重写 observer 的更新函数来传递信息。


3.委托相关设计


目前为止我对设计模式的理解是程序员们针对反复出现的相似问题,总结出的开发经验。

本周课程提到的设计模式有1.1的Adapter,1.3的Handle/Body(Pimpl),2.1的Templa Method以及2.3的Observer


3.1Composite

Composite设计模式适用于设计树形结构的系统,比如file system。以下描述以文件系统为例子,具体的结构和代码如下图

  

class Primitive:表示基础部分、基础物,比如文件;

class Composite:表示组合物、容器,可以容纳Primitive,也可以容纳自己,可以理解为文件夹,既可以容纳文件,也可以容纳文件夹;

但是这造成了一个问题,如何来实现呢?通过容器来实现这个功能,这个容器不仅能够装载文件还能装载文件夹,因此有了Primitive和Composite的父类Component,容器只要装载Component的指针就能装载文件和文件夹,即委托

class Component:表示Primitive和Composite的共同父类,

add函数:Composite类必须能够加进文件和文件夹,因此add函数必须定义为虚函数。但是不能定义为纯虚函数,因为文件不能加入文件,文件夹可以,所以add函数写成空函数。


通过上面的例子可以归纳出Composite模式使用的一种情况:当我们想表示对象的“部分-整体”层次结构,也就是说希望忽略单个对象和组合对象的不同,统一使用结构中的所有对象。

缺点:因为忽略了单个对象和组合对象的不同,因此需要在父类中定义一些公共操作,并提供缺省的实现,而单个对象和组合对象可以对它们进行重定义。这种定义方式会与类层次结构设计原则冲突,该原则规定:一个类只能定义那些对它放入子类有意义的操作。



3.2Prototype

Prototype模式用来处理如何在一个继承体系中创建未来才会出现的子类的问题。

《设计模式》中提到过:使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。

注意:原型模式是通过拷贝自身来创建新的对象。

首先把原型的设计思路理一遍:

1.为了让未来创建的子类能够将自己上传到父类中去,子类在类定义中声明一个静态子类变量,通过私有的构造函数(这里要注意构造函数),调用addPrototype函数,将子类型的指针(this)上传到父类的数组或指针容器中;


父类存放子类原型的数组:


通过以上操作,父类中就有了子类的原型,以指针的形式存放在数组中,产生的关系是委托,也只有委托才能完成这个任务,不同大小的子类指针大小相同,因此可以存放在同一数组或容器中;

2.父类如何来产生子类呢?父类中的findAndClone函数承担了这个任务,通过调用findAndClone函数clone出子类,但是这里要注意的一点是原先的构造函数用来上传原型了,如果再次调用又会上传原型,因此这里重新定义一个构造函数,只有函数的参数个数不同就行具体如下图所示

父类的findAndClone函数



子类的clone和新构造函数:



通过以上两步操作基本实现了父类创建未来出现的子类的功能。


一点思考:通过3.1、3.2的两种设计模式,对类与类之间的委托关系有了更深的理解,我们可以通过指针来解决子类大小不同无法在父类中统一表示的问题,使得我们的操作更加灵活。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值