c++中代理类的学习

https://blog.csdn.net/lcg910978041/article/details/51468680

C++代理类是为了解决这样的问题: 容器通常只能包含一种类型的对象,所以很难在容器中存储对象本身。

怎样设计一个c++容器,使它有能力包含类型不同而彼此相关的对象? 

代理运行起来和他所代表的对象基本相同,但是允许将整个派生层次压缩在一个对象类型中。

代理类的每个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象。通过在容器中使用代理对象而不是对象本身的方式,就是代理类的精髓思想所在。

1.为什么c++需要代理类

考虑如下的一个小实例:假设有一个类,命名为RoadVehicle,代表陆地上的车辆,简单的定义如下:

  1. //定义陆地上的车辆
  2. class RoadVehicle
  3. {
  4. public:
  5. RoadVehicle(){} //默认构造函数,容许声明RoadVehicle的数组
  6. double get_weight()const
  7. {
  8. return 0.0;
  9. }
  10. };

现在我们需要用一个容器或者数组来保存这个类的一些列对象,那么我们可以如下声明数组来存放这些对象:

  1. RoadVehicle parking_lot[100];

这样做会造成一个很明显的问题:当我们有另一类车辆    (比如飞机类AirCraft) 时,我们将RoadVehicle类和AirCraft类继承自同一个父类Vehicle,有如下的结构:

  1. class Vehicle
  2. {
  3. public:
  4. virtual double get_weight()const = 0;
  5. };
  6. //定义陆地上的车辆
  7. class RoadVehicle:public Vehicle
  8. {
  9. public:
  10. RoadVehicle(){} //默认构造函数,容许声明RoadVehicle的数组
  11. double get_weight()const
  12. {
  13. return 0.0;
  14. }
  15. };
  16. //定义飞机
  17. class AirCraft:public Vehicle
  18. {
  19. public:
  20. AirCraft(){} //默认构造函数,容许声明AirCraft的数组
  21. double get_weight()const
  22. {
  23. return 1.0;
  24. }
  25. };

有如上继承结构出现时,我们再来考虑如何用一个容器或者数组存放这些对象,尽管可以声明多个数组来存放,如下:

  1. RoadVehicle parking_lot1[100];
  2. AirCraft parking_lot2[100];


但是当我们的车辆的类型越来越多,比如还有AutoVehicle类,Helicopter类等等时,这种方式需要声明对应的数组来存放,很显然这不符合c++的精神。

既然RoadVehicle类和AirCraft类均继承自Vehicle类,那我们声明如下的数组来存放这些对象,又会发生什么事情呢?

  1. Vehicle parking_lot[100];

但是这又会带来两个问题:

第一:Vehicle是一个抽象类,它拥有一个纯虚函数,它不能生成对象,因此他无法定义这样的数组;

第二:即使我们在vehicle中将纯虚函数修改为虚函数,使它可以生成对象,也会出现问题,比如有如下代码:

  1. RoadVehicle x;  
  2. parking_lot[num] = x;  


此时,会把x转换成一个Vehicle对象,同时会丢失所有在Vehicle类中没有的成员,然后将剪裁了的对象复制到parking_lot数组中去

解决上述两个问题的方法非常简单,即存储Vehicle对象的指针:

  1. Vehicle * parking_lot[100];  

此时,上述操作变为:

  1. RoadVehicle x;  
  2. parking_lot[num] = &x;  


但是,这种方法又带来了新的问题:

由于x是局部变量,当x超出其作用域时,parking_lot数组中的指针变成了野指针这一问题可以如下解决:

  1. RoadVehicle x;  
  2. parking_lot[num] = new RoadVehicle(x);  //即:使parking_lot中的指针指向x的一个副本  

但上述方法实施的前提是我们明确的知道了要放入parking_lot中的对象的静态类型,在本例中,我们知道要放入parking_lot的是RoadVehicle,而不是AirCraft或其他的类对象。

当我们不知道要放入parking_lot中的对象的静态类型时,比如,我们我们想让parking_lot[p]指向一个新建立的Vehicle,并且这个Vehicle和parking_lot[q]中的对象相同,这时我们并不知道parking_lot[q]中指针所指的对象的静态类型,那我们该如何复制parking_lot[q]所指向的副本给parking_lot[p]呢?

显然:

  1. if(p != q)  
  2. {  
  3.     delete parking_lot[p];  
  4.     parking_lot[p] = parking_lot[q];  
  5. }  

这是不行的,这样会导致parking_lot[p]和parking_lot[q]指向同一个对象

  1. if(p != q)  
  2. {  
  3.     delete parking_lot[p];  
  4.     parking_lot[p] = new Vehicle(parking_lot[q]);  (这样的构造   是不对的   1 Vehicle无法产生对象     2 即使产生也不一定对   几乎可能会被裁剪掉  )
  5. }  


这也是不行的,因为Vehicle无法产生对象,即使能产生,也是被剪裁了的,是Vehicle对象, 而不是parking_lot[q]所指的对象的类型。  (因为具体指向的类型  我们不清楚)

事实上,这里的根本原因在于,我们无法知道parking_lot[q]所指对象的静态类型,c++中解决这一类问题是用多态。我们需要一个克隆函数当parking_lot[q]调用自己的克隆函数时,复制自己的一个副本,并返回一个该副本的指针。因此我们的代码变为如下:

  1. #include <iostream>
  2. using namespace std;
  3. class Vehicle
  4. {
  5. public:
  6. virtual double get_weight()const = 0;
  7.     virtual Vehicle* copy() const = 0;  
  8. };
  9. //定义陆地上的车辆
  10. class RoadVehicle:public Vehicle
  11. {
  12. public:
  13. RoadVehicle(){} //默认构造函数,容许声明RoadVehicle的数组
  14. RoadVehicle(const RoadVehicle& RV){}
  15. double get_weight()const
  16. {
  17. return 0.0;
  18. }
  19.     Vehicle* copy()const  
  20.     {  
  21.         return new RoadVehicle(*this);  
  22.     }  
  23. };
  24. //定义飞机
  25. class AirCraft:public Vehicle
  26. {
  27. public:
  28. AirCraft(){} //默认构造函数,容许声明AirCraft的数组
  29. AirCraft(const AirCraft& AC){}
  30. double get_weight()const
  31. {
  32. return 0.0;
  33. }
  34.     Vehicle* copy()const  
  35.     {  
  36.         return new AirCraft(*this);  
  37.     }  
  38. };

由于parking_lot数组中存放的都是Vehicle类型的指针,而其所实际指向的对象是Vehicle的任意子类因此,在释放parking_lot中指针所指向的内存时,我们需要虚析构函数,否则,会造成内存泄露,因此我们为每个类添加虚析构函数。代码如下:

  1. #include <iostream>
  2. using namespace std;
  3. class Vehicle
  4. {
  5. public:
  6. virtual double get_weight()const = 0;
  7. virtual Vehicle* copy() const = 0;
  8.     virtual ~Vehicle(){}  
  9. };
  10. //定义陆地上的车辆
  11. class RoadVehicle:public Vehicle
  12. {
  13. public:
  14. RoadVehicle(){} //默认构造函数,容许声明RoadVehicle的数组
  15. RoadVehicle(const RoadVehicle& RV){}
  16. double get_weight()const
  17. {
  18. return 0.0;
  19. }
  20. Vehicle* copy()const
  21. {
  22. return new RoadVehicle(*this);
  23. }
  24. ~RoadVehicle(){}
  25. };
  26. //定义飞机
  27. class AirCraft:public Vehicle
  28. {
  29. public:
  30. AirCraft(){} //默认构造函数,容许声明AirCraft的数组
  31. AirCraft(const AirCraft& AC){}
  32. double get_weight()const
  33. {
  34. return 0.0;
  35. }
  36. Vehicle* copy()const
  37. {
  38. return new AirCraft(*this);
  39. }
  40. ~AirCraft(){}
  41. };

如此一来,上面存在的问题,可以如下解决:

  1. if(p != q)
  2. {
  3.     delete parking_lot[p];  
  4.     parking_lot[p] = parking_lot[q]->copy();  //这一点 很重要!!!!!!!!!!!!   别忘记 这一步骤    delete parking_lot[p];  
  5. }


上面的方法,在一定程度上解决了我们的问题,但是它在parking_lot数组中存储的是指针,这使得用户显示的处理内存分配,那么有没有一种方法既能够不显示的处理内存分配,又能够保持类Vehicle在运行时动态绑定呢?这就是我们的代理类了。

=======================================================================================================

我们为Vehicle类创建一个代理类,假设命名为VehicleSurrogate,每个VehicleSurrogate对象代表一个Vehicle对象只要VehicleSurrogate对象存在,那么它所关联的Vehicle对象就存在,复制VehicleSurrogate对象就会复制相对应的Vehicle对象,给代理赋新值也会先删除旧的关联的对象,再复制新的对象。

我们可以如下定义代理类:

  1. class VehicleSurrogate
  2. {
  3. public:
  4. VehicleSurrogate():vp(0){} //默认构造函数,使得可以声明VehicleSurrogate的数组  
  5. VehicleSurrogate(const VehicleSurrogate& VS)//以自身对象初始化   {  
  6.         vp  =   VS.vp   ?    (VS.vp->copy())  : (0);  
  7. }
  8. VehicleSurrogate(const Vehicle& V):vp(V.copy()){}    //以它所代理的Vehicle对象初始化        这两个就是 定义了不同掉copy构造函数
  9. VehicleSurrogate& operator=(const VehicleSurrogate& VS)//重载赋值  {
  10. if(this != &VS)  {  //看看是不是自己    防止自我复制
  11. delete this->vp;
  12.     this->vp = VS.vp?VS.vp->copy():0;  
  13. }
  14. return *this;
  15. }
  16. ~VehicleSurrogate()  {
  17.       delete vp;  
  18. }
  19.     //代理Vehicle类行为的函数,Vehicle有多少个行为函数,这里就需要重新定义多少个代理类函数  
  20. double get_weight()const  {
  21. return vp->get_weight();
  22. }
  23. private:  
  24.     Vehicle* vp;  
  25. };


有了上述代理类之后,我们就可以如下操作: 

  1. VehicleSurrogate parking_lot[100];      !!!数组的定义会调用  num 次的默认构造函数 
  2.   
  3. RoadVehicle x;  
  4. parking_lot[0] = x;   注意:1先调用copy构造函数构造临时对象  上面两个copy选择其中一个                                                                                                                                               2再调用代理类的  operator =   函数(因为VehicleSurrogate 前面定义数组时候已经定义且默认初始化了  所以调用   = 函数 
  5. cout << parking_lot[0].get_weight();  

至此,我们利用代理类,就可以即不用显示进行内存分配管理,又可以使得Vehicle子类对象进行动态绑定。

========================================================================!!!!!!!!!!!!!!!!!

2. 智能指针

“c++代理类(一)”中完成的简单代理类虽然解决了最急迫的问题,但效率上又存在了另外的问题,该简单类存在的问题主要是:

每个代理类对象都唯一关联一个实际对象,代理类对象存在则实际对象存在,代理类对象释放则实际类对象也要释放,且复制代理类对象就必须要复制实际类对象。

这在实际类很大的时候复制开销是非常大的。而且,代理类的复制会频繁的发生,比如:作为函数的参数进行值传递,或者作为函数的返回值等等。

我们将对该简单代理类进行改进,改进的思想主要是:在代理类中为其代理的实际对象添加一个标记,该标记指出有多少个代理类对象代理了这个实际对象,这样当我们复制代理类对象时,其实际所代理的对象就不需要复制了,只需要修改该标记即可。

其具体做法如下:

  1. class VehicleSurrogate
  2. {
  3. public:
  4. ......//跟简单类一样,唯一不同的是需要加入处理标记num的部分
  5. private:
  6. Vehicle* vp;
  7.     int * num; //添加的标记字段  
  8. };


有了如上的结构,每次复制代理类时,只需要将(*num)++就可以了。其意义为:绑定到实际对象的代理类又多了一个

上述的策略在不需要修改实际对象时非常有用,即所有代理类对象只是读它所代理的实际对象时,但当某个代理类需要修改它所代理的实际对象时,问题就发生了,由于所有代理类对象实际所代理的对象在内存中是同一份,因此,一个代理类对象所做的修改将会影响其他代理类,因此,此时需要对所代理的实际对象进行复制,且该复制是无法避免的。我们称之为——写时复制。

实现指针的类代码如下:

  1. #include <iostream>
  2. using namespace std;
  3. class Vehicle
  4. {
  5. public:
  6. Vehicle():weight(0.0){} //默认构造函数
  7. Vehicle(double w):weight(w){}   //含参构造函数
  8. virtual ~Vehicle(){}    //虚析构函数必须存在,所有子类对象在析构时都是以Vehicle*的方式调用析构函数的,以虚析构函数调用才能调用到正确的析构函数  才不会导致内存泄露
  9. virtual Vehicle* copy()const  //复制自己  {
  10. return new Vehicle(*this);
  11. }
  12. //读
  13. double get_weight()const   {
  14. return weight;
  15. }
  16. //写
  17. Vehicle* set_weight(double w)  {
  18. weight = w;
  19. return this;
  20. }
  21. private:
  22. double weight;
  23. };
  24. //定义陆地上的车辆
  25. class RoadVehicle:public Vehicle
  26. {
  27. public:
  28. RoadVehicle(){} //默认构造函数,容许声明RoadVehicle的数组
  29. RoadVehicle(double w):Vehicle(w){}
  30. RoadVehicle(const RoadVehicle& RV):Vehicle(RV.get_weight()){} //拷贝构造函数
  31. Vehicle* copy()const   //复制自己  {
  32. return new RoadVehicle(*this);
  33. }
  34. ~RoadVehicle(){}
  35. };
  36. //定义飞机
  37. class AirCraft:public Vehicle
  38. {
  39. public:
  40. AirCraft(){} //默认构造函数,容许声明AirCraft的数组
  41. AirCraft(double w):Vehicle(w){}
  42. AirCraft(const AirCraft& AC):Vehicle(AC.get_weight()){} //拷贝构造函数
  43. Vehicle* copy()const
  44. {
  45. return new AirCraft(*this);
  46. }
  47. ~AirCraft(){}
  48. };
  49. //智能指针类定义
  50. class VehicleSurrogate
  51. {
  52. public:
  53. VehicleSurrogate():vp(new Vehicle()),num(new int(1)){} //默认构造函数,使得可以声明VehicleSurrogate的数组
  54. VehicleSurrogate(const VehicleSurrogate& VS):vp(VS.vp),num(VS.num)//拷贝构造函数,可发现此时它所代理的实际对象并未复制
  55. {
  56. ++(*num);
  57. }
  58. VehicleSurrogate(const Vehicle& V):vp(V.copy()),num(new int(1)){}//以它所代理的Vehicle对象初始化
  59. VehicleSurrogate& operator=(const VehicleSurrogate& VS)//重载赋值,可发现此时它所代理的实际对象并未复制
  60. {
  61. if(this != &VS)
  62. {
  63. //删除原来的旧的关联对象
  64. if(--(*num) == 0 )
  65. {
  66. delete vp;
  67. delete num;
  68. }
  69. //赋值新的关联对象
  70. vp = VS.vp;
  71. num = VS.num;
  72. ++(*num);
  73. }
  74. return *this;
  75. }
  76. ~VehicleSurrogate()
  77. {
  78. if(--(*num)== 0)
  79. {
  80. delete vp;
  81. delete num;
  82. }
  83. }
  84. int get_num()const
  85. {
  86. return *num;
  87. }
  88. //代理Vehicle类行为的函数,读操作无需复制所代理的实际对象
  89. double get_weight()const
  90. {
  91. return vp->get_weight();
  92. }
  93. //写时复制策略,写时必须复制所代理的实际对象
  94. VehicleSurrogate& set_weight(double w)
  95. {
  96. if((*num) == 1)
  97. {
  98. vp->set_weight(w);
  99. }
  100. else
  101. {
  102. --(*num);
  103. vp = vp->copy();//真正的复制发生在这里
  104. num = new int(1);
  105. vp->set_weight(w);
  106. }
  107. return *this;
  108. }
  109. private:
  110. Vehicle* vp;
  111. int * num;
  112. };
  113. int main()
  114. {
  115. //测试上述智能指针
  116. VehicleSurrogate parking_lot[100];
  117. RoadVehicle x(10);
  118. parking_lot[0] = RoadVehicle(x);
  119. parking_lot[1] = parking_lot[0];
  120. parking_lot[0].set_weight(5.0);
  121. cout << parking_lot[0].get_weight()<<endl<<parking_lot[0].get_num()<<endl;
  122. cout << parking_lot[1].get_weight()<<endl<<parking_lot[1].get_num()<<endl;
  123. }

使用智能指针,既保留了简单代理类的优点:无需显示管理内存分配,且能实现所代理的实际对象动态绑定,又省略了过多的复制开销。

转载:

http://blog.csdn.net/liqianyuan2009/article/details/15815341

http://blog.csdn.net/liqianyuan2009/article/details/16345121

更多代理参考:

http://blog.csdn.net/wuzhekai1985/article/det

http://www.cnblogs.com/jiese/p/

原文地址:https://www.cnblogs.com/zhangkele/p/9236612.html

时间: 2024-11-13 11:08:38

c++中代理类的学习的相关文章

ios 中代理类汇总

ios 代理类总结一下.今后多看看 UITextViewDelegate UIToolbarDelegate UITextInputDelegate UITextFieldDelegate UIWebViewDelegate NSLayoutManagerDelegate //UIActionSheet //UIActionSheetDelegate 要放弃 //UIAlertView  要放弃 //UIAlertViewDelegate //优先使用preferred //UIAlertCon

C++中代理类和句柄类

指针是 C 与其他语言区别的重要特征之一,在 C++ 中,指针也被广泛运用,我们通过指针实现多态.然而,众所周知,指针的使用必须小心,否则很容易造成内存泄漏 Memory Leak.当我们有几个指针指向同一个对象时有其应该注意,关于何时释放这个对象:(1) 如果释放的太早,那么其它的指针仍然指向这片内存,如果再使用它会造成未定义行为.(2) 如果一直不释放可能会丢失最后一个指向这个对象的指针 导致内存无法被释放. 用 C++ 的方法来解决这种问题就是建立一个类来包含需要管理的指针 ,由于这些类往

c++学习笔记5,多重继承中派生类的构造函数与析构函数的调用顺序(二)

现在来测试一下在多重继承,虚继承,MI继承中虚继承中构造函数的调用情况. 先来测试一些普通的多重继承.其实这个是显而易见的. 测试代码: //测试多重继承中派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace std; class base { public: base() { cout<<"base created!"<<endl; }

Java中String类学习总结

java中String类的使用频率非常高,本人在学习此模块时,认为下列几点知识值得注意: 一.String是不可变对象 java.lang.String类使用了final修饰,不能被继承.Java程序中的所有字面值,即双引号括起的字符串,如"abc",都是作为String类的实例实现的.String是常量,其对象一旦构造就不能再被改变.换句话说,String对象是不可变的,每一个看起来会修改String值的方法,实际上都是创造了一个全新的String对象,以包含修改后的字符串内容.而最

Java中的静态代理、通用动态代理类以及原理剖析

代理模式和静态代理 在开发中,代理模式是常用的模式之一,一般来说我们使用的代理模式基本上都是静态代理,实现模式大致如下 : 我们以网络代理为例,简单演示一下静态代理的实现 : // 网络接口 interface Network { public void surfTheInternet(); public void gotoFacebook(); } // 普通网络 class CommonNetwork implements Network { @Override public void su

Scala学习(五)---Scala中的类

Scala中的类 摘要: 在本篇中,你将会学习如何用Scala实现类.如果你了解Java或C++中的类,你不会觉得这有多难,并且你会很享受Scala更加精简的表示法带来的便利.本篇的要点包括: 1. 类中的字段自动带有getter方法和setter方法 2. 你可以用定制的getter/setter方法替换掉字段的定义,而不必修改使用类的客户端,这就是所谓的"统一访问原则" 3. 用@BeanProperty注解来生成JavaBeans的getXxx/setXxx()方法 4. 每个类

Spring Aop中,获取被代理类的工具

在实际应用中,顺着过去就是一个类被代理.反过来,可能需要逆向进行,拿到被代理的类,实际工作中碰到了,就拿出来分享下. 1 /** 2 * 获取被代理类的Object 3 * @author Monkey 4 */ 5 public Object getTarget(Object proxy) throws Exception { 6 7 if(!AopUtils.isAopProxy(proxy)) { 8 //不是代理对象 9 return proxy; 10 } 11 12 if(AopUt

设计模式学习---代理类

今天继续看了代理模式,总结一下,觉得这个代理类主要是通过一个借口抽象了一些代理类和被代理类公用的方法.然后让被代理类实现这些方法. 再让被代理类通过代理类中来调用这些方法. 代码如下:PersonA表示真正追求美女的人,PersonB则是PersonA与美女的传话人,就是代理类. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.

python cookbook第三版学习笔记十三:类和对象(五)代理类以及内存回收

代理类: 代理类的作用其实有继承有些类似,如果你想将某个实例的属性访问代理到内部另外一个实例中去,可以用继承也可以用代理.来看下代理的应用: class A:     def spam(self,x):         print 'in Class A x=%d' % x     def foo(self):         print 'in Class A:foo()' class B1:     def __init__(self):         self._a=A()