C++代理 Surrogate

容器通常只能包含一种类型的对象,所以很难在容器中存储对象本身。存储指向对象的指针,虽然允许通过继承来处理类型不同的问题( 多态性 ),但是也增加了内存分配的额外负担。所以我们通过定义名为 代理 的对象来解决该问题 。代理 运行起来和它所代表的对象基本相同,但是允许将整个派生层次压缩在一个对象类型中。

假设有一个表示不同种类的交通工具的类派生层次:

class Vehicle
{
public:virtual double weight() const = 0;virtual void start() = 0;//...
};class RoadVehicle:public Vehicle{/*...*/};
class AutoVehicle:public Vehicle{/*...*/};
class Aircraft:public Vehicle{/*...*/};
class Helicopter:public Vehicle{/*...*/};

可见Vehicle是一个抽象基类,有两个纯虚函数表示一些共有属性。下面请看下面这句话为什么不能达到预期的效果:

Vehicle parking_lot[1000];

表面上看是由于Vehicle是一个抽象基类,因此,只有从类Vehicle派生出来的类才能实例化,类Vehicle本身不会有对象,自然也就不会有对象数组了。

       但是,假设我们剔除了类Vehicle中的所有纯虚函数,使其对象存在,那又会出现什么样的情况呢?看下面的语句:

Automobile x=/*...*/;

parking_lot[num_vehicles++] = x;

把x赋给parking_lot的元素,会把x转换成一个Vehicle对象,同时会丢失所有在Vehicle类中没有的成员。该赋值语句还会把这个被剪裁了的对象复制到parking_lot数组中去。这样,我们只能说parking_lot是Vehicle的集合,而不是所有继承自Vehicle的对象的集合。

经典解决方案------提供一个间接层

最早的合适的间接层形式就是存储指针,而不是对象本身:

Vehicle* parking_lot[1000];

然后,就有

Automobile x = /*...*/;

parking_lot[num_vehicles++] = &x;

这种方法解决了迫切的问题,但是也带来了两个新问题。

我们存储在parking_lot中的是指向x的指针,在上例中是一个局部变量。这样,一旦变量x没有了【作用域】,parking_lot就不知道指向什么东西了。

我们可以这么变通一下,放入parking_lot中的值,不是指向原对象的指针,而是指向它们的副本的指针。当我们释放parking_lot时,也释放其中所指向的全部对象。

上述修改虽然不用存储指向本地对象的指针,但是它也带来了动态内存管理的负担。另外,只有当我们知道要放到parking_lot中的对象的静态类型后,这种方法才起作用。不知道又会怎样呢?看下面的:

if(p != q)

{

   delete parking_lot[p];

   parking_lot[p] = parking_lot[q];

}

这样的话,parking_lot[p]和parking_lot[q]将指向相同的对象,这不是我们想要的。在看下面的行不行:

if(p != q)

{

   delete parking_lot[p];

   parking_lot[p] = new Vehicle(*parking_lot[q]);

}

这样我们又回到了前面的问题:没有Vehicle类型的对象,即使有,也不是我们想要的(是经过剪裁后的对象)。

如何复制编译时类型未知的对象-------虚复制函数

我们在上面的Vehicle类中加入一个合适的纯虚函数:

class Vehicle 

public: 
virtual double weight() const = 0; 
virtual void start() = 0;

virtual Vehicle* copy() const = 0; 
 //... 
};

接下来,在每个派生自Vehicle的类中添加一个新的成员函数copy。指导思想就是,如果vp指向某个继承自Vehicle的不确定类的对象,那么vp->copy()会获得一个指针,该指针指向该对象的一个新建的副本。例如:如果Truck继承自(间接或直接)类Vehicle,则它的copy函数就类似于:

Vehicle* Truck::copy() const

{

    return new Truck(*this);

}

当然,处理完一个对象后,需要清除该对象。要做到这一点,就必须确保类Vehicle有一个虚析构函数:

class Vehicle 

public: 
virtual double weight() const = 0; 
virtual void start() = 0;

virtual Vehicle* copy() const = 0;

virtual ~Vehicle(){} 
 //... 
}; 
有了上面的分析,下面我们就来定义代理类:

class VehicleSurrogate
{
public:
  VehicleSurrogate();
  VehicleSurrogate(const Vehicle&);
  ~VehicleSurrogate();
  VehicleSurrogate(const VehicleSurrogate&);
  VehicleSurrogate& operator = (const VehicleSurrogate&);
private:
  Vehicle* vp;
};

上述代理类有一个以const Vehicle&为参数的构造函数,这样就能为任意继承自Vehicle的类的对象创建代理了 (多态性,因为这里是引用参数) 。同时,代理类还有一个缺省构造函数,所以我们能够创建VehicleSurrogate对象的数组。

然而, 缺省构造函数也给我们带来了问题:如果Vehicle是个抽象基类,我们应该如何规定VehicleSurrogate的缺省操作呢?它所指向的对象的类型是什么呢?不可能是Vehicle,因为根本就没有Vehicle对象。为了得到一个更好的方法,我们要引入行为类似于零指针的空代理的概念。能够创建、销毁和复制这样的代理,但是进行其他的操作就视为出错。

下面看各个函数的定义:

VehicleSurrogate::VehicleSurrogate():vp(0){}

VehicleSurrogate::VehicleSurrogate(const Vehicle& v):vp(v.copy()){}//非零的检测室必要的,空代理

VehicleSurrogate::~VehicleSurrogate()

{

      delete vp;//C++标准里面对一个空指针运用delete也是没有问题的


VehicleSurrogate::VehicleSurrogate(const VehicleSurrogate& v):vp(v.vp?v.vp->copy():0){}

VehicleSurrogate& VehicleSurrogate::operator=(const VehicleSurrogate& v)

{

    if(this!=&v)//对赋值操作符进行检测,确保没有将代理赋值给它自身

    {

      delete vp;

      vp=(v.vp?v.vp->copy():0);//非零的检测是必要的,空代理

    }

    return *this;

}

下面就很容易定义我们的数组了:

VehicleSurrogate parking_lot[1000];

Automobile x;

parking_lot[num_vehicles++] = x;

最后一条语句就等价于

parking_lot[num_vehicles++] = VehicleSurrogate(x);

这个语句创建了一个关于对象x的副本,并将VehicleSurrogate对象绑定到该副本,然后将这个对象赋值给parking_lot的一个元素。当最后销毁parking_lot数组时,所有这些副本也将被清除。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ Handle(句柄) part1

本文是我学习C++沉思录第6章的笔记 本文主要讲述了Handle类的概念,定义方法以及写时复制技术。 在前文(Surrogate代理类)的讲解中我们了解到了代理的实现方法. 代理类有很多好处,但是麻烦的是每次都得进行复制.如果该类是经常使用并且member很…

sscanf的高级用法

sscanf的高级用法(总结) 2012-04-25 18:50:25分类: C/C sscanf(recvbuf,"%*[^/]/%[^ ]s",buf_rev); sscanf(buf, "GET /%[^ ]", buf_rev);这个是在一个webserver.c里面的例子,通过sscanf(&#xf…

选择排序 冒泡排序 二分查找

选择排序 int [] arr {2,48,28,32,90,12}; for&#xff08;int i 0&#xff1b; i < arr.length - 1;i&#xff09;{ for(int j i 1; j < arr.length;j){ if(arr[i] < arr[j]){ int c; c arr[i]; arr[i] arr[j]; arr[j] c; } } } 冒泡排序 for(int i 0;i <…

C++, ID、指针、handle (void *)的区别

原文链接&#xff1a; http://hi.baidu.com/dandanfeng160/blog/item/4eaa3df5215bc42dbd310955.html 在Windows程序设计中&#xff0c;句柄是无法精确定义的术语。随便找一个高手&#xff0c;让他给你讲讲句柄是什么&#xff0c;恐怕他都很难给你一个具体的定义来。 在Wind…

Swift调用Objective C的FrameWork

很多Github的库经过很多年的发展&#xff0c;源码都是OC写的&#xff0c;&#xff0c;所以&#xff0c;用Swift调用OC的库就是开发中难免遇到的的一个问题&#xff0c;本文以AFNetworking为例&#xff0c;讲解如何跨语言调用。 第一步 创建一个空的工程 注意&#xff0c;语言选…

命令行 java文本编辑工具 重载 内存区域 栈 堆

一、dir 列出当前目录下的文件以及文件夹 md创建目录 rd删除目录 cd 进入指定目录 cd..返回到上一级目录 &#xff1a; 切换盘符 比如&#xff1a; F: 二、editPlus 编写程序 三、重载&#xff1a;在同一个class中&#xff0c;出现了函数名称相同&#xff0…

数据结构(Java)——查找和排序(1)

1.查找的定义 查找是这样一个过程&#xff0c;即在某个项目组中寻找某一指定目标元素&#xff0c;或者确定该组中并不存在该目标元素。 对其进行查找的项目的组有时也成为查找池。两种常见的查找方式&#xff1a;线性查找和二分查找。为了能够查找某一对象&#xff0c;我们就必…

GetProcAddress()用法

函数功能描述: GetProcAddress()函数检索指定的动态链接库(DLL)中的输出库函数地址。 函数原型&#xff1a; FARPROC GetProcAddress( HMODULE hModule, // DLL模块句柄 LPCSTR lpProcName // 函数名 ); 参数&#xff1a; hModule [in] 包含此函数的…

支付宝问题LaunchServices: ERROR: There is no registered handler for URL scheme alipay

LaunchServices: ERROR: There is no registered handler for URL scheme alipay &#xff08;这句话其实是在告诉你 设备上没有安装 支付宝的客户端,此时会走网页端&#xff09;而有人会发现并没有HTML5网页弹出过一会&#xff0c;会发现服务器返回4000支付失败&#xff0c;这…

C++string类常用函数 c++中的string常用函数用法总结

string类的构造函数&#xff1a; string(const char *s); //用c字符串s初始化 string(int n,char c); //用n个字符c初始化 此外&#xff0c;string类还支持默认构造函数和复制构造函数&#xff0c;如string s1&#xff1b;string s2"hello"&#xff1b;都是正…

排列与组合

话说&#xff0c;初一的时候看到这样一道题&#xff1a;有一种彩票中奖率为1%&#xff0c;买一百张是不是一定能中奖&#xff1f;答案自然是否定的&#xff0c;但我在想&#xff0c;如果有200张彩票&#xff0c;两张有奖&#xff0c;买一百张中奖率是多少&#xff1f;一天晚上睡…

剔除服务器返回的NSNull格式的数据

服务器返回NSNull格式的数据&#xff0c;真。。的烦人 解决办法&#xff1a;在AFN请求里面加上下面两段代码&#xff0c;OK AFJSONResponseSerializer *response (AFJSONResponseSerializer *)manager.responseSerializer; response.removesKeysWithNullValues YES;

显式(静态)调用: LIB + DLL + .H

1、编程时用ad.h,ad.lib,放在项目当前目录里2、在头文件中加入#include "ad.h"3、在Project Setting–>Link–>Object/library modules加入ad.lib执行时将ad.dll跟你的程序放在同一目录。 就可以直接调用dll中的函数了 当前目录 转载于:https://www.cnblogs.co…

boost Mutex

写过多线程程序的人都知道&#xff0c;不能让多个线程同时访问共享的资源是至关重要的。 假如一个线程试图改变共享数据的值&#xff0c;而另外一个线程试图去读取该共享数据的值&#xff0c;结果将是未定义的。 为了阻止这样的事情发生&#xff0c;需要用到一些非凡的原始数据…

接入支付宝出现交易订单处理失败,请稍后再试(ALI64)的错误

上次在接入支付宝的时候就碰到了交易订单处理失败&#xff0c;请稍后再试&#xff08;ALI64&#xff09;这样的错误&#xff0c;后来经过排查和总结&#xff0c;一般来讲这种问题都是公钥和私钥没有正确配置造成的。支付宝这边为了保证数据在传输时不被篡改&#xff0c;使用了r…

c中session的用法

c中session的用法你知道吗&#xff1f;下面小编就跟你们详细介绍下c中session的用法&#xff0c;希望对你们有用。c中session的用法如下&#xff1a;Session的基本属性&#xff1a;一、属性1、SessionIDSessionID 属性返回用户的会话标识。在创建会话时&#xff0c;服务器会为每…

查看硬件信息

测试机器的硬件信息&#xff1a; 查看CPU信息&#xff08;型号&#xff09; # cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c 8 Intel(R) Xeon(R) CPU E5410 2.33GHz (看到有8个逻辑CPU, 也知道了CPU型号) # cat /proc/cpuinfo | grep physical …

支付宝集成交互流程

交互流程 功能流程 流程说明&#xff08;以Android平台为例&#xff09;&#xff1a; 第4步&#xff1a;调用支付接口&#xff1a;此消息就是本接口所描述的开发包提供的支付对象PayTask&#xff0c;将商户签名后的订单信息传进pay方法唤起支付宝收银台&#xff0c;订单格式具体…

VxLAN基础

转自&#xff1a;http://blog.csdn.net/freezgw1985/article/details/16354897 一 . 为什么需要Vxlan1. vlan的数量限制4096个vlan远不能满足大规模云计算数据中心的需求2. 物理网络基础设施的限制基于IP子网的区域划分限制了需要二层网络连通性的应用负载的部署3. TOR交换机MA…

find_first_of()和 find_last_of() 【获取路径、文件名】

string 类提供字符串处理函数&#xff0c;利用这些函数&#xff0c;程序员可以在字符串内查找字符&#xff0c;提取连续字符序列(称为子串)&#xff0c;以及在字符串中删除和添加。我们将介绍一些主要函数。 1.函数find_first_of()和 find_last_of() 执行简单的模式匹配&#x…