C++代理

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

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

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

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

  1. RoadVehicleparking_lot[100];

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

  1. classVehicle
  2. {
  3. public:
  4. virtualdoubleget_weight()const=0;
  5. };
  6. //定义陆地上的车辆
  7. classRoadVehicle:publicVehicle
  8. {
  9. public:
  10. RoadVehicle(){}//默认构造函数,容许声明RoadVehicle的数组
  11. doubleget_weight()const
  12. {
  13. return0.0;
  14. }
  15. };
  16. //定义飞机
  17. classAirCraft:publicVehicle
  18. {
  19. public:
  20. AirCraft(){}//默认构造函数,容许声明AirCraft的数组
  21. doubleget_weight()const
  22. {
  23. return1.0;
  24. }
  25. };
当有如上继承结构出现时,我们再来考虑如何用一个容器或者数组存放这些对象,尽管可以声明多个数组来存放,如下:

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

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

  1. Vehicleparking_lot[100];

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

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

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

  1. RoadVehiclex;
  2. parking_lot[num]=x;
此时,会把x转换成一个Vehicle对象,同时会丢失所有在Vehicle类中没有的成员,然后将剪裁了的对象复制到parking_lot数组中去。

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

  1. Vehicle*parking_lot[100];
此时,上述操作变为:

  1. RoadVehiclex;
  2. parking_lot[num]=&x;
但是,这种方法又带来了新的问题:

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

  1. RoadVehiclex;
  2. parking_lot[num]=newRoadVehicle(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. deleteparking_lot[p];
  4. parking_lot[p]=parking_lot[q];
  5. }

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

  1. if(p!=q)
  2. {
  3. deleteparking_lot[p];
  4. parking_lot[p]=newVehicle(parking_lot[q]);
  5. }
这也是不行的,因为Vehicle无法产生对象,即使能产生,也是被剪裁了的,是Vehicle对象, 而不是parking_lot[q]所指的对象的类型。

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

  1. #include<iostream>
  2. usingnamespacestd;
  3. classVehicle
  4. {
  5. public:
  6. virtualdoubleget_weight()const=0;
  7. virtualVehicle*copy()const=0;
  8. };
  9. //定义陆地上的车辆
  10. classRoadVehicle:publicVehicle
  11. {
  12. public:
  13. RoadVehicle(){}//默认构造函数,容许声明RoadVehicle的数组
  14. RoadVehicle(constRoadVehicle&RV){}
  15. doubleget_weight()const
  16. {
  17. return0.0;
  18. }
  19. Vehicle*copy()const
  20. {
  21. returnnewRoadVehicle(*this);
  22. }
  23. };
  24. //定义飞机
  25. classAirCraft:publicVehicle
  26. {
  27. public:
  28. AirCraft(){}//默认构造函数,容许声明AirCraft的数组
  29. AirCraft(constAirCraft&AC){}
  30. doubleget_weight()const
  31. {
  32. return0.0;
  33. }
  34. Vehicle*copy()const
  35. {
  36. returnnewAirCraft(*this);
  37. }
  38. };
由于parking_lot数组中存放的都是Vehicle类型的指针,而其所实际指向的对象是Vehicle的任意子类,因此,在释放parking_lot中指针所指向的内存时,我们需要虚析构函数,否则,会造成内存泄露,因此我们为每个类添加虚析构函数。代码如下:

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


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

  1. if(p!=q)
  2. {
  3. deleteparking_lot[p];
  4. parking_lot[p]=parking_lot[q]->copy();
  5. }


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

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

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

  1. classVehicleSurrogate
  2. {
  3. public:
  4. VehicleSurrogate():vp(0){}//默认构造函数,使得可以声明VehicleSurrogate的数组
  5. VehicleSurrogate(constVehicleSurrogate&VS)//以自身对象初始化
  6. {
  7. vp=VS.vp?VS.vp->copy():0;
  8. }
  9. VehicleSurrogate(constVehicle&V):vp(V.copy()){}//以它所代理的Vehicle对象初始化
  10. VehicleSurrogate&operator=(constVehicleSurrogate&VS)//重载赋值
  11. {
  12. if(this!=&VS)
  13. {
  14. deletethis->vp;
  15. this->vp=VS.vp?VS.vp->copy():0;
  16. }
  17. return*this;
  18. }
  19. ~VehicleSurrogate()
  20. {
  21. deletevp;
  22. }
  23. //代理Vehicle类行为的函数,Vehicle有多少个行为函数,这里就需要重新定义多少个代理类函数
  24. doubleget_weight()const
  25. {
  26. returnvp->get_weight();
  27. }
  28. private:
  29. Vehicle*vp;
  30. };
有了上述代理类之后,我们就可以如下操作:

  1. VehicleSurrogateparking_lot[100];
  2. RoadVehiclex;
  3. parking_lot[0]=x;
  4. cout<<parking_lot[0].get_weight();

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


2. 智能指针

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

每个代理类对象都唯一关联一个实际对象,代理类对象存在则实际对象存在,代理类对象释放则实际类对象也要释放,且复制代理类对象就必须要复制实际类对象。这在实际类很大的时候复制开销是非常大的。而且,代理类的复制会频繁的发生,比如:作为函数的参数进行值传递,或者作为函数的返回值等等。

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

其具体做法如下:

  1. classVehicleSurrogate
  2. {
  3. public:
  4. ......//跟简单类一样,唯一不同的是需要加入处理标记num的部分
  5. private:
  6. Vehicle*vp;
  7. int*num;//添加的标记字段
  8. };
有了如上的结构,每次复制代理类时,只需要将(*num)++就可以了。其意义为:绑定到实际对象的代理类又多了一个

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

实现指针的类代码如下:

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

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

转载:

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/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值