怎样在一个容器中包含类型不同,但是彼此有关系的对象?众所周知,C++的容器只能存放类型相同的元素,所以直接在一个容器中存储不同类型的对象本身是不可能的,只能通过以下两种方案实现:

1. 提供一个间接层,在容器中存放对象的指针而不是对象本身。

2. 通过代理类实现。

class Animal
{
public:
virtual void Eat() = ;
}; class Cat : public Animal{};
class Dog : public Animal{};
class Bird : public Animal{};

在上面我们看到有一个虚基类和三个继承类,下面分别用两种方案来实现一个容器存放不同类型但又互相关联的类。

1.通过指针实现

Animal* animals_array[];
Cat cat;
Dog dog;
Bird bird;
animals_array[] = &cat;
animals_array[] = &dog;
animals_array[] = &bird;

这样会带来一个问题,就是容器中的指针指向的对象如果被销毁,这个指针就会变成野指针,就像下面这样:

Animal* animals_array[];
do
{
Cat cat;
Dog dog;
Bird bird;
animals_array[] = &cat;
animals_array[] = &dog;
animals_array[] = &bird;
}while();
//此时对象已经被析构,容器中的指针指向未知内容

也可以换一种方式,构造一个新的动态对象,将其地址放在容器中,这样就可以避免对象析构导致指针失效的问题:

Cat cat;
animals_array[] = new Cat(cat);

这样会曾加额外的内存开销,并且可能出现容器中两个指针同时指向一个对象的情况。
所以,在容器中存放不同对象的指针并不是一个很好的解决方案。

2.通过代理类实现

实现代码如下:

class Animal
{
public:
virtual void Eat() = ; //copy函数,构造一个基于自身对象类型的对象
virtual Animal* copy() const = ; virtual ~Animal() {}
}; class Cat : public Animal
{
public:
virtual void Eat()
{
std::cout << "cat eat." << std::endl;
} virtual Animal* copy() const
{
// 返回一个以自身作为参数构造的Cat类型的对象
return new Cat(*this);
}
}; class Dog : public Animal
{
public: virtual void Eat()
{
std::cout << "dog eat." << std::endl;
} virtual Animal* copy() const
{
// 返回一个以自身作为参数构造的Dog类型的对象
return new Dog(*this);
}
}; class Bird : public Animal
{
public: virtual void Eat()
{
std::cout << "bird eat." << std::endl;
} virtual Animal* copy() const
{
// 返回一个以自身作为参数构造的Bird类型的对象
return new Bird(*this);
}
}; //代理类
class AnimalSurrogate
{
public:
AnimalSurrogate() :pa(NULL) {} AnimalSurrogate(const Animal& ani)
{
pa = ani.copy();
} //拷贝构造
AnimalSurrogate(const AnimalSurrogate& ani_srg)
{
pa = ani_srg.pa != nullptr ? ani_srg.pa->copy() : nullptr;
} ~AnimalSurrogate()
{
if (pa != nullptr)
{
delete pa;
pa = nullptr;
}
} //重载 = 操作符
AnimalSurrogate& operator=(const AnimalSurrogate& ani_srg)
{
if (this != &ani_srg)
{
delete pa;
pa = ani_srg.pa != nullptr ? ani_srg.pa->copy() : nullptr;
}
return *this;
} //将基类中的公共函数搬过来,这样就可以通过代理类直接访问这些方法
void Eat()
{
if (pa == nullptr)
{
throw "empty AnimalSurrogate.Eat()";
}
return pa->Eat();
} private:
Animal* pa;//存储基类的指针
};

通过代码可以看出来,所谓的代理类,就是构造一个新的类,这个类中包含关联的基类类型的指针,该指针可以指向不同类型但又相互关联的子类对象,通过指针可以转调对象的方法,同时实现内存的管理。代理类的实用方法如下:

Cat cat;
Dog dog;
Bird bird; arr[] = AnimalSurrogate(cat);
arr[] = AnimalSurrogate(dog);
arr[] = AnimalSurrogate(bird); arr[].Eat();//输出 cat eat.
arr[].Eat();//输出 dog eat.
arr[].Eat();//输出 bird eat.

总结:代理类的的每个对象都代表另一个对象,该对象可以使位于一个完成继承层次中的任何类的对象。通过在容器中用代理对象而不是对象本身的方式,实现容器中存放不同类型的对象

使用代理类的优缺点如下:

  • 优点:使用代理类比直接在容器中存放不同对象的指针更安全,便于实现内存管理。
  • 缺点:内存开销太大,放入容器的每个对象都需要拷贝一次,不够灵活。

为了避免对象的拷贝,可以通过句柄类来实现,关于句柄类的原理和使用在下一篇问文章中作介绍。

C++的代理类的更多相关文章

  1. vs2013 手动生成webservice代理类wsdl

    第一步: 第二步: 第三步: 至此wsdl代理类生成成功!

  2. Struts2 源码分析——Action代理类的工作

    章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...

  3. iOS Class 使用NSProxy和NSObject设计代理类的差异

    经常发现在一些需要使用消息转发而创建代理类时, 不同的程序员都有着不同的使用方法, 有些采用继承于NSObject, 而有一些采用继承自NSProxy. 二者都是Foundation框架中的基类, 并 ...

  4. WCF通过SVCUtil.exe生成客户端代理类和配置文件(转)

    WCF服务调用通过两种常用的方式: 1:一种是借助代码生成工具SvcUtil.exe或者添加服务引用的方式. 2:一种是通过ChannelFactory直接创建服务代理对象进行服务调用. 本文只针对通 ...

  5. 终于解决:升级至.NET 4.6.1后VS2015生成WCF客户端代理类的问题

    在Visual Studio 2015中将一个包含WCF引用的项目的targetFramework从4.5改为4.6.1的时候,VS2015会重新生成WCF客户端代理类.如果WCF引用配置中选中了&q ...

  6. Web Service代理类生成工具

    本文原文连接:http://www.cnblogs.com/dengxinglin/p/3334158.html 之前一篇文章写 Web Service服务代理类生成及编译 , 通过命令行的方式可以直 ...

  7. Web Serveice服务代理类生成及编译

    本文链接地址:http://www.cnblogs.com/dengxinglin/p/3334158.html 一.生成代理类 对于web service服务和wcf的webservice服务,我们 ...

  8. 解析利用wsdl.exe生成webservice代理类的详解

    利用wsdl.exe生成webservice代理类:根据提供的wsdl生成webservice代理类1.开始->程序->Visual Studio 2005 命令提示2.输入如下红色标记部 ...

  9. 【C++沉思录】代理类

    1.考虑下面的场景:设计一个容器,包含一组类型不同但相互关联的对象(比如:Animal,Dog,Cat),对象具备多态行为.2.容器一般只能包含一种类型的对象,使用vector<Animal&g ...

  10. EF容器---代理类对象

    #region 修改--官方的修改是,先查询,然后修改 /// <summary> /// 修改--官方的修改是,先查询,然后修改 /// </summary> static ...

随机推荐

  1. 基于 HTML5 Canvas 的工控机柜 U 位动态管理

    前言 U 是一种表示服务器外部尺寸的单位,是 unit 的缩略语,详细的尺寸由作为业界团体的美国电子工业协会(EIA)所决定.之所以要规定服务器的尺寸,是为了使服务器保持适当的尺寸以便放在铁质或铝质的 ...

  2. window.open open new window?

    when ever i use window.location.href=//some url it always open a new window, this only happens when ...

  3. 一篇文章带你了解Cloud Native

    背景 Cloud Native表面看起来比较容易理解,但是细思好像又有些模糊不清:Cloud Native和Cloud关系是啥?它用来解决什么问题?它是一个新技术还是一个新的方法?什么样的APP符合“ ...

  4. Django的时区问题

    在Django项目中,最好全部日期值都做成配时区信息的,但是由于遗留项目或者跨语言项目,其他语言的开发人员觉得时区信息处理太麻烦.如何在一个项目中同时适配带时区和不带时区的两种字段. 1.输出:不带时 ...

  5. 部署网站: 配置项目到iis上运行报目录错误

    配置项目到iis上运行报目录错误 以下三种方法可使用: 1.添加文件访问权限 everyone  (线上环境慎用) 2.重新注册iis 3.web.config 加一段话 在<system.we ...

  6. C# Dispose模式详细分析

    C#Dispose模式 目的: 为了及时释放宝贵的非托管资源和托管资源,并且保证资源在被gc回收的时候可以正确释放资源,同时兼顾执行效率 必须遵循的事实: 1 托管资源释放: 由另一线程的gc进行释放 ...

  7. Myeclipse使用git

    推荐使用服务器:coding,coding的上传很稳定很快 怎么建仓库: coding 这个服务器呢有个很明显的东西就是太的语言可以是中文的, 点头标签的加号就能进行创建仓库了 点击新建就行了 点击这 ...

  8. webpack4.x版本splitChunksPlugin的配置项详解与实际应用场景

    在工程化地使用webpack时,公共代码抽离是不可或缺的,4.x版本之后,commonsChunkPlugin已经被去掉,splitChunksPlugins登上舞台,并且优化了很多配置选项,集体课件 ...

  9. Java工具类 通过ResultSet对象返回对应的实体List集合

    自从学了JDBC用多了像一下这种代码: List<xxx> list = new Array<xxx>(); if(rs.next()){ xxx x = new xxx(); ...

  10. 杨老师课堂_Java核心技术下之控制台模拟记事本案例

    预览效果图: 背景介绍: 编写一个模拟记事本的程序通过在控制台输入指令,实现在本地新建文件打开文件和修改文件等功能. 要求在程序中: 用户输入指令1代表"新建文件",此时可以从控制 ...