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

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

2. 通过代理类实现。

  1. class Animal
  2. {
  3. public:
  4. virtual void Eat() = ;
  5. };
  6.  
  7. class Cat : public Animal{};
  8. class Dog : public Animal{};
  9. class Bird : public Animal{};

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

1.通过指针实现

  1. Animal* animals_array[];
  2. Cat cat
  3. Dog dog;
  4. Bird bird;
  5. animals_array[] = &cat;
  6. animals_array[] = &dog;
  7. animals_array[] = &bird;

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

  1. Animal* animals_array[];
  2. do
  3. {
  4. Cat cat;
  5. Dog dog;
  6. Bird bird;
  7. animals_array[] = &cat;
  8. animals_array[] = &dog;
  9. animals_array[] = &bird;
  10. }while();
  11. //此时对象已经被析构,容器中的指针指向未知内容

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

  1. Cat cat;
  2. animals_array[] = new Cat(cat);

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

2.通过代理类实现

实现代码如下:

  1. class Animal
  2. {
  3. public:
  4. virtual void Eat() = ;
  5.  
  6. //copy函数,构造一个基于自身对象类型的对象
  7. virtual Animal* copy() const = ;
  8.  
  9. virtual ~Animal() {}
  10. };
  11.  
  12. class Cat : public Animal
  13. {
  14. public:
  15. virtual void Eat()
  16. {
  17. std::cout << "cat eat." << std::endl;
  18. }
  19.  
  20. virtual Animal* copy() const
  21. {
  22. // 返回一个以自身作为参数构造的Cat类型的对象
  23. return new Cat(*this);
  24. }
  25. };
  26.  
  27. class Dog : public Animal
  28. {
  29. public:
  30.  
  31. virtual void Eat()
  32. {
  33. std::cout << "dog eat." << std::endl;
  34. }
  35.  
  36. virtual Animal* copy() const
  37. {
  38. // 返回一个以自身作为参数构造的Dog类型的对象
  39. return new Dog(*this);
  40. }
  41. };
  42.  
  43. class Bird : public Animal
  44. {
  45. public:
  46.  
  47. virtual void Eat()
  48. {
  49. std::cout << "bird eat." << std::endl;
  50. }
  51.  
  52. virtual Animal* copy() const
  53. {
  54. // 返回一个以自身作为参数构造的Bird类型的对象
  55. return new Bird(*this);
  56. }
  57. };
  58.  
  59. //代理类
  60. class AnimalSurrogate
  61. {
  62. public:
  63. AnimalSurrogate() :pa(NULL) {}
  64.  
  65. AnimalSurrogate(const Animal& ani)
  66. {
  67. pa = ani.copy();
  68. }
  69.  
  70. //拷贝构造
  71. AnimalSurrogate(const AnimalSurrogate& ani_srg)
  72. {
  73. pa = ani_srg.pa != nullptr ? ani_srg.pa->copy() : nullptr;
  74. }
  75.  
  76. ~AnimalSurrogate()
  77. {
  78. if (pa != nullptr)
  79. {
  80. delete pa;
  81. pa = nullptr;
  82. }
  83. }
  84.  
  85. //重载 = 操作符
  86. AnimalSurrogate& operator=(const AnimalSurrogate& ani_srg)
  87. {
  88. if (this != &ani_srg)
  89. {
  90. delete pa;
  91. pa = ani_srg.pa != nullptr ? ani_srg.pa->copy() : nullptr;
  92. }
  93. return *this;
  94. }
  95.  
  96. //将基类中的公共函数搬过来,这样就可以通过代理类直接访问这些方法
  97. void Eat()
  98. {
  99. if (pa == nullptr)
  100. {
  101. throw "empty AnimalSurrogate.Eat()";
  102. }
  103. return pa->Eat();
  104. }
  105.  
  106. private:
  107. Animal* pa;//存储基类的指针
  108. };

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

  1. Cat cat;
  2. Dog dog;
  3. Bird bird;
  4.  
  5. arr[] = AnimalSurrogate(cat);
  6. arr[] = AnimalSurrogate(dog);
  7. arr[] = AnimalSurrogate(bird);
  8.  
  9. arr[].Eat();//输出 cat eat.
  10. arr[].Eat();//输出 dog eat.
  11. 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. Web系统测试Web安全性测试

    WEB安全性测试介绍WEB安全性测试--拒绝服务攻击WEB安全性测试--文件上传漏洞WEB安全性测试--跨站攻击WEB安全性测试--SQL注入一WEB安全性测试--SQL注入二WEB安全性测试--SQ ...

  2. 8 个最好的 jQuery 树形 Tree 插件

    由于其拥有庞大,实用的插件库,使得 jQuery 变得越来越流行.今天将介绍一些最好的 jQuery 树形视图插件,具有扩展和可折叠的树视图.这些都是轻量级的,灵活的 jQuery 插件,它将一个无序 ...

  3. 【转】H.264RTP封包原理

    原文地址:H.264RTP封包原理   作者:cnp11 1.  引言  随着信息产业的发展,人们对信息资源的要求已经逐渐由文字和图片过渡到音频和视频,并越来越强调获取资源的实时性和互动性.但人们又面 ...

  4. POSTGRESQL 并发控制

    http://meidayhxp.blog.163.com/blog/static/117608156201210243837491/ 这个内容是官方Doc中的一章,具体是那一版的,还未确认. 第九章 ...

  5. (原创)ubuntu 10.04+ruby1.9.2+rails3 安装记录

    第一步当然是现在ruby 1.9.2 的sourcecode了,因为现在的ubuntu 源中还没有1.9.2的版本 我下载的是ruby-1.9.2-p290.tar.gz 然后解压到/usr/loca ...

  6. 爬取廖雪峰的python3教程

    从廖雪峰老师的python教程入门的,最近在看python爬虫,入手了一下 代码比较low,没有用到多线程和ip代理池 然后呢,由于robots.txt的限定,构建了一些user-agent,并放慢的 ...

  7. 启动eclipse时候提示错误Error:Could not create the Java Virtual Machine. Error:A Fatal exception has occurred,Program will exit.

    我的是neon3版本 解决办法是: 首先把这两个选项勾选,才能看到eclipse.ini完整的文件名.然后用记事本等工具打开编辑. 新版的里面原本是这样: -startup plugins/org.e ...

  8. java之jsp内置对象

    1.out对象 <% out.println("金鳞岂是池中物,<br>"); out.println("一遇风云变化龙.<br>" ...

  9. mysql查询当天所有数据sql语句

    mysql查询当天的所有信息: select * from test where year(regdate)=year(now()) and month(regdate)=month(now()) a ...

  10. 你不知道的JavaScript--Item8 函数,方法,构造函数调用

    1.函数调用 Function绝对是JavaScript中的重中之重.在JavaScript中,Function承担了procedures, methods, constructors甚至是class ...