1.句柄类存储和管理指针是C++一个通用的技术。指针所指对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类类型对象。用户通过句柄类访问继承层次的操作。句柄的用户可以获得动态行为而不需操心指针的管理。

2.句柄类经常需要在不知道对象的确切类型时分配已知对象的副本,为了支持句柄,需要从基类开始,在继承层次的每个类型中增加clone,基类必须将该函数定义为虚函数,以获得动态行为。

3. clone函数

clone函数很重要,要使用句柄类该是不可避免的需要定义clone函数

回想为什么要使用句柄类,一开始,想用multiset来存储某个类的一些对象,这些对象可能存在父子关系,所以不能直接存放对象(子对象转化为父对象将丢失派生部分),那么只能存放指针,然而指针的管理麻烦(这点我倒是不清楚到底有多难)。这样就发明了句柄类,建一个类,该类包含两个指针,一个指针p指向一个希望存储的对象,另一个指针指向一个size_type类型的use整数存放该对象的引用数(引用数的作用即管理指针何时释放)。

回到我们的问题,clone函数干什么用,是这样的,对于一个我们希望存储的对象,我们要把这个对象跟句柄类关联起来才能存储。

那么有两种办法,一,直接把这个对象的地址赋值给句柄类里的p指针(即指向对对象的那个),二,用这个对象复制一个对象,再把这个复制出来的对象拿来跟p关联,

现在我来告诉你,第一种方法不可行:这个原始对象从哪来的我们不确定,如果这个对象的空间管理是由系统管理,那么我们不知道它何时就被释放了,也许能挺到main函数结束,也许在某个函数调用结束,它被释放了我们还能指向它么。或者这个对象用户还希望使用在很多地方,却由于use为0而被我们手工的释放了,这也是灾难。再说,这个对象要是再被拿去初始化另一个句柄类,那么造成的情况是我们有两群,是两群,看清楚了,这两群句柄对象,他们关联到同一个需要存储的对象,却各自有一个计数,相互独立,带来的问题是,该对象的释放如何统一?

所以,我们只能用第二种方法,我是说,我们的需要存储的对象与句柄类对象关联起来,我们用的方法是,用这个对象复制出一个新对象,而把这个新对象与句柄对象关联(重复一下:关联的方法就是把新建对象的地址赋值给句柄对象的p指针)。好了,对于某个我们希望关联到句柄类对象的对象,我们首先要复制它!耐心点,接下来便要讲到clone函数的意义了。在代码中,我们是如何关联的:在我们的句柄对象的构造函数里,传过来的的是需要存储的那群对象的基类引用,我们的构造函数要实现的功能就是根据这个引用复制一个与引用指向的对象相同的对象,问题是,引用他可能指向那一群对象中的任何一个,而接受引用的只是基类对象引用,如何根据基类引用接受的参数去复制派生类对象,你发现没有,在句柄类中是实现不了的!基类引用或指针根本无法调用派生类的方法,再重复,根据一个基类对象引用无法复制一个派生类对象出来。怎么办呢?我们复制不出来,让它自己复制一个给我们不就行了么,所以,关于复制对象的实现,不是在句柄类里,而是在需要被复制的对象所属的类里,那群对象各自有各自所属的类,在它们各自的类里都定义一个clone函数,当然,是虚函数,然后用基类引用接受了那群对象里的某一个,让该对象调用clone,根据多态的特性,它会调用自身所属的类得clone函数,用这个函数,当然可以复制出跟自身一摸一样对象,复制对象的目的就达到了,这就是clone函数的由来。

a) Item_base类是真正用于记录的类

#ifndef ITEM_H
#define ITEM_H
#include<string>class Item_base{
public:Item_base(const std::string &book=" ",double sales_price=0.0):isbn(book),price(sales_price){}std::string book() const{return isbn;}void set_price(double new_price){price=new_price;}//价格计算函数virtual double net_price(size_t n)const{return n*price;}virtual Item_base* clone() const{//return new Item_base(*this);}virtual ~Item_base(){}
private:std::string isbn;
protected:double price;
};//抽象类
//保存折扣率和可实行折扣策略的数量,派生类将使用这些数据实现定价策略
class Disc_item: public Item_base{
public://Disc_item(const std::string &book=" ",double sales_price=0.0,size_t qty=0,double disc_rate=0.0):Item_base(book,sales_price),quantity(qty),discount(disc_rate){}double net_price(size_t n) const =0;std::pair<size_t,double> discount_policy() const{return std::make_pair(quantity,discount);}
protected:size_t quantity;//可执行折扣策略的购买量double discount;//折扣率
};//批量购买折扣类
class Bulk_item : public Disc_item {
public:Bulk_item (const std::string &book=" ",double sales_price=0.0, size_t qty=0,double disc_rate=0.0):Disc_item(book,sales_price,qty,disc_rate){}//注意使用基类的构造函数double net_price(size_t cnt) const{if(cnt>quantity)return cnt*(1-discount)*price;elsereturn cnt*price;}virtual Item_base* clone() const{//供句柄类动态调用的构造函数return new Bulk_item(*this);}
};//有限折扣类:购买量不超过阈值,则给折扣,超出部分原价购买
class Lds_item :public Disc_item{
public:Lds_item(const std::string &book=" ",double sales_price=0.0,size_t qty=0,double disc_rate=0.0):Disc_item(book,sales_price,qty,disc_rate){}double net_price(size_t cnt) const{if(cnt<=quantity)return cnt*(1-discount)*price;elsereturn cnt*price-quantity*discount*price;}virtual Item_base* clone() const {return new Lds_item(*this);}
};
#endif

b) Sales_item类是句柄类,用于包装Item_base并管理Item_base的指针和内存释放

#ifndef SALES_ITEM_H
#define SALES_ITEM_H
#include "Item.h"class Sales_item{
public://默认构造函数,创建未绑定的句柄Sales_item(): p(0),use(new size_t (1) ) {}//将创建绑定到Item_base对象副本的句柄Sales_item(const Item_base &item): p(item.clone()), use(new size_t(1))  {  }//复制控制成员管理使用计数和指针Sales_item(const Sales_item &i):p(i.p),use(i.use){++*use;}~Sales_item(){decr_use();}Sales_item& operator=(const Sales_item &);//重载操作符const Item_base *operator->() const{if(p)return p;}const Item_base &operator*(){if(p)return *p;}void change_price(double new_price){p->set_price(new_price);}
private:Item_base *p;//指向共享Item_base对象的指针size_t *use;//指向共享的使用计数的指针//为析构函数和赋值操作符使用的辅助函数void decr_use(){if(--*use==0)delete p;delete use;}
};Sales_item& Sales_item::operator=(const Sales_item& rhs){++*rhs.use;decr_use();p=rhs.p;use=rhs.use;return *this;
}
#endif

c) Basket类是进行实际计算的类,它将通过句柄类计算书的总价,而不关心实际类Item_base的内存释放

注意这里multiset容器的使用方法:首先传递给容器一个比较函数,用于对键值进行顺序存储(保证相同ISBN的书放在一起);然后使用upper_bound(*iter)和items.count(*iter)进行了价格的计算。

#ifndef BASKET_H
#define BASKET_H#include "Sales_item.h"
#include <set>//compare 函数用于比较对象,以确定Basket的multiset成员中各元素的排列次序
inline bool compare(const Sales_item &lhs,const Sales_item &rhs){return lhs->book() < rhs->book();//按照ISBN进行排序
}class Basket {typedef bool (*Comp)( const Sales_item &lhs, const Sales_item &rhs);
public:typedef std::multiset<Sales_item,Comp> set_type;//multiset中可以指定比较函数————1typedef set_type::size_type size_type;typedef set_type::const_iterator const_iter;Basket(): items(compare){ };//初始化比较器——————————————————————2void add_item(const Sales_item &item){items.insert(item);}size_type size(const Sales_item &i) const {return items.count(i);}double total() const;//
private:std::multiset< Sales_item,Comp > items;
};double Basket::total()const{double sum=0.0;for(const_iter iter=items.begin();iter!=items.end();iter=items.upper_bound(*iter)){//upper_bound寻找不等于*iter的最右边界sum+=(*iter)->net_price(items.count(*iter));   }return sum;
}#endif

main.cpp

#include<iostream>
#include "Basket.h"
#include "Sales_item.h"
#include "Item.h"
using namespace std;int main(){Basket basket;Sales_item item1(Bulk_item("7-115-14554-7", 99, 2, 0.2));Sales_item item2(Item_base("7-115-14554-8", 39));Sales_item item3(Lds_item ("7-115-14554-9", 59, 200, 0.2));Sales_item item4(Bulk_item("7-115-14554-7", 99, 2, 0.2));Sales_item item5(Bulk_item("7-115-14554-7", 10, 2, 0.2));basket.add_item(item1);cout<<basket.total()<<endl;basket.add_item(item2);cout<<basket.total()<<endl;basket.add_item(item3);cout<<basket.total()<<endl;basket.add_item(item4);cout<<basket.total()<<endl;basket.add_item(item5);cout<<basket.total()<<endl;return 0;
}

Makefile

objects=main.orun:$(objects)g++ -o run $(objects)main.o:      Sales_item.h Item.h Basket.h.PHONY:clean
clean:rm run $(objects)

面向对象编程实例——句柄类的使用相关推荐

  1. System Verilog面向对象编程(OPP)基础——类(class)的基本使用

    该文主要是笔者梳理绿皮书对应章节的内容 System Verilog面向对象编程OPP基础--类(class)的基本使用 面向对象编程 概述 考虑名词而非动词 编写第一个类class OOP术语 创建 ...

  2. 【面向对象编程】(4) 类的继承,重构父类中的方法

    各位同学好,今天和大家分享一下面向对象编程中,类的三大特征之继承.主要介绍:子类继承父类的基本方法:重写父类的类方法:重构父类的初始化方法:super() 方法.本节主要是单继承,多继承在下一节中介绍 ...

  3. 【面向对象编程】(3) 类之间的交互,依赖关系,关联关系

    各位同学好,今天和大家分享一下面向对象编程中,类之间的交互,类之间的依赖关系和关联关系.有不明白的可见前一章节:https://blog.csdn.net/dgvv4/article/details/ ...

  4. c语言面向对象编程中的类_C ++中的面向对象编程

    c语言面向对象编程中的类 Object oriented programming, OOP for short, aims to implement real world entities like ...

  5. Java面向对象编程篇1——类与对象

    Java面向对象编程篇1--类与对象 1.面向过程 1.1.概念 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了 1.2.优缺点 优点:性 ...

  6. C语言面向对象编程的类是指,c语言面向对象编程中的类_C ++中的面向对象编程...

    c语言面向对象编程中的类 Object Oriented programming is a programming style that is associated with the concept ...

  7. js 面向对象编程实例

    当我们在写js代码的时候,总是会想能否和java一样面向对象去编程,让人更易维护以及代码的分离.下面举一例说明: jsp页面如下 <!DOCTYPE html> <html>& ...

  8. python面向对象编程中方法和属性_Python面向对象编程中关于类和方法的学习笔记...

    Python面向对象编程中关于类和方法的学习笔记 类与类方法是面向对象的编程语言中必不可少的特性,本文总结了Python面向对象编程中关于类和方法的学习笔记,需要的朋友可以参考下 类和实例 pytho ...

  9. python面向对象编程中_Python面向对象编程中关于类和方法

    类和实例 python是一个面向对象的语言,而面向对象最重要的概念就是类和实例, 记得刚学习的时候不太理解这些概念,直到老师说了一句"物以类聚". 没错就是类, 归类 物以类聚 类 ...

  10. 简述对象和类的关系python_(一)Python入门-6面向对象编程:02类的定义-类和对象的关系-构造函数-实例属性-实例方法...

    一:类的定义 如果把对象比作一个"饼干",类就是制造这个饼干的"模具". 我们通过类定义数据类型的属性(数据)和方法(行为),也就是说,"类将行为和状 ...

最新文章

  1. Java8读文件仅需一行代码
  2. Find a way
  3. Mysql Incorrect string value问题解决
  4. CF1267G-Game Relics【数学期望,dp】
  5. opencore0.6.3_Ubuntu 18.04 源码编译安装 PHP 7.3
  6. jQuery-DOM操作
  7. GPU并行计算OpenCL(2)——矩阵卷积
  8. 厉害了!他是 Facebook 开国元老,30 分钟解决 Instagram 服务器挂机难题
  9. 在OneNote中快速插入当前日期和时间
  10. Python教学视频(二)输出语句的使用
  11. python文本聚类分析作用_文本聚类应用意义
  12. matlab 平滑曲线连接_用MATLAB做数据拟合究竟有多直观
  13. 个人博客毕业设计设计总结
  14. Laravel学习 - Eloquent\Builder与Query\Builder
  15. 大家都在努力,你凭什么不努力
  16. python3GUI--翻译器By:PyQt5(附源码)
  17. 单片机io口定义C语言,怎样把单片机的8个不同管脚定义成同一个IO口??
  18. 云服务器挂软件用哪个系统,云服务器挂软件用什么系统
  19. QT 版puremvc框架
  20. java写日历记事本系统_用Java实现日历记事本源代码2660【新版】

热门文章

  1. linux异步io缺陷,具有libaio性能问题的Linux异步IO
  2. 手机日历便签怎么设置日历开始于周几?
  3. linux 透明图片,FreeImage 生成带透明通道的GIF
  4. 关于举办“2022年(第15届)中国大学生计算机设计大赛”通知
  5. mastercam西门子840d后处理_MasterCAM对西门子802D后置处理
  6. MySQL定时备份数据库(全库备份)
  7. Tecplot绘制流体后处理图的问题
  8. 极大似然估计和交叉熵
  9. 基于FPGA的波束形成verilog开发
  10. 【信号与系统】信号频谱和测量之汉明窗