1.为什么c++需要代理类
考虑如下的一个小实例:假设有一个类,命名为RoadVehicle,代表陆地上的车辆,简单的定义如下:
- //定义陆地上的车辆
- classRoadVehicle
- {
- public:
- RoadVehicle(){}//默认构造函数,容许声明RoadVehicle的数组
- doubleget_weight()const
- {
- return0.0;
- }
- };
现在我们需要用一个容器或者数组来保存这个类的一些列对象,那么我们可以如下声明数组来存放这些对象:
- RoadVehicleparking_lot[100];
这样做会造成一个很明显的问题:当我们有另一类车辆(比如飞机类AirCraft)时,我们将RoadVehicle类和AirCraft类继承自同一个父类Vehicle,有如下的结构:
- classVehicle
- {
- public:
- virtualdoubleget_weight()const=0;
- };
- //定义陆地上的车辆
- classRoadVehicle:publicVehicle
- {
- public:
- RoadVehicle(){}//默认构造函数,容许声明RoadVehicle的数组
- doubleget_weight()const
- {
- return0.0;
- }
- };
- //定义飞机
- classAirCraft:publicVehicle
- {
- public:
- AirCraft(){}//默认构造函数,容许声明AirCraft的数组
- doubleget_weight()const
- {
- return1.0;
- }
- };
- RoadVehicleparking_lot1[100];
- AirCraftparking_lot2[100];
既然RoadVehicle类和AirCraft类均继承自Vehicle类,那我们声明如下的数组来存放这些对象,又会发生什么事情呢?
- Vehicleparking_lot[100];
但是这又会带来两个问题:
第一:Vehicle是一个抽象类,它拥有一个纯虚函数,它不能生成对象,因此他无法定义这样的数组;
第二:即使我们在vehicle中将纯虚函数修改为虚函数,使它可以生成对象,也会出现问题,比如有如下代码:
- RoadVehiclex;
- parking_lot[num]=x;
解决上述两个问题的方法非常简单,即存储Vehicle对象的指针:
- Vehicle*parking_lot[100];
- RoadVehiclex;
- parking_lot[num]=&x;
由于x是局部变量,当x超出其作用域时,parking_lot数组中的指针变成了野指针。这一问题可以如下解决:
- RoadVehiclex;
- parking_lot[num]=newRoadVehicle(x);//即:使parking_lot中的指针指向x的一个副本
当我们不知道要放入parking_lot中的对象的静态类型时,比如,我们我们想让parking_lot[p]指向一个新建立的Vehicle,并且这个Vehicle和parking_lot[q]中的对象相同,这时我们并不知道parking_lot[q]中指针所指的对象的静态类型,那我们该如何复制parking_lot[q]所指向的副本给parking_lot[p]呢?
显然:
- if(p!=q)
- {
- deleteparking_lot[p];
- parking_lot[p]=parking_lot[q];
- }
这是不行的,这样会导致parking_lot[p]和parking_lot[q]指向同一个对象
- if(p!=q)
- {
- deleteparking_lot[p];
- parking_lot[p]=newVehicle(parking_lot[q]);
- }
事实上,这里的根本原因在于,我们无法知道parking_lot[q]所指对象的静态类型,c++中解决这一类问题是用多态。我们需要一个克隆函数,当parking_lot[q]调用自己的克隆函数时,复制自己的一个副本,并返回一个该副本的指针。因此我们的代码变为如下:
- #include<iostream>
- usingnamespacestd;
- classVehicle
- {
- public:
- virtualdoubleget_weight()const=0;
- virtualVehicle*copy()const=0;
- };
- //定义陆地上的车辆
- classRoadVehicle:publicVehicle
- {
- public:
- RoadVehicle(){}//默认构造函数,容许声明RoadVehicle的数组
- RoadVehicle(constRoadVehicle&RV){}
- doubleget_weight()const
- {
- return0.0;
- }
- Vehicle*copy()const
- {
- returnnewRoadVehicle(*this);
- }
- };
- //定义飞机
- classAirCraft:publicVehicle
- {
- public:
- AirCraft(){}//默认构造函数,容许声明AirCraft的数组
- AirCraft(constAirCraft&AC){}
- doubleget_weight()const
- {
- return0.0;
- }
- Vehicle*copy()const
- {
- returnnewAirCraft(*this);
- }
- };
- #include<iostream>
- usingnamespacestd;
- classVehicle
- {
- public:
- virtualdoubleget_weight()const=0;
- virtualVehicle*copy()const=0;
- virtual~Vehicle(){}
- };
- //定义陆地上的车辆
- classRoadVehicle:publicVehicle
- {
- public:
- RoadVehicle(){}//默认构造函数,容许声明RoadVehicle的数组
- RoadVehicle(constRoadVehicle&RV){}
- doubleget_weight()const
- {
- return0.0;
- }
- Vehicle*copy()const
- {
- returnnewRoadVehicle(*this);
- }
- ~RoadVehicle(){}
- };
- //定义飞机
- classAirCraft:publicVehicle
- {
- public:
- AirCraft(){}//默认构造函数,容许声明AirCraft的数组
- AirCraft(constAirCraft&AC){}
- doubleget_weight()const
- {
- return0.0;
- }
- Vehicle*copy()const
- {
- returnnewAirCraft(*this);
- }
- ~AirCraft(){}
- };
如此一来,上面存在的问题,可以如下解决:
- if(p!=q)
- {
- deleteparking_lot[p];
- parking_lot[p]=parking_lot[q]->copy();
- }
上面的方法,在一定程度上解决了我们的问题,但是它在parking_lot数组中存储的是指针,这使得用户显示的处理内存分配,那么有没有一种方法既能够不显示的处理内存分配,又能够保持类Vehicle在运行时动态绑定呢?这就是我们的代理类了。
我们为Vehicle类创建一个代理类,假设命名为VehicleSurrogate,每个VehicleSurrogate对象代表一个Vehicle对象,只要VehicleSurrogate对象存在,那么它所关联的Vehicle对象就存在,复制VehicleSurrogate对象就会复制相对应的Vehicle对象,给代理赋新值也会先删除旧的关联的对象,再复制新的对象。
我们可以如下定义代理类:
- classVehicleSurrogate
- {
- public:
- VehicleSurrogate():vp(0){}//默认构造函数,使得可以声明VehicleSurrogate的数组
- VehicleSurrogate(constVehicleSurrogate&VS)//以自身对象初始化
- {
- vp=VS.vp?VS.vp->copy():0;
- }
- VehicleSurrogate(constVehicle&V):vp(V.copy()){}//以它所代理的Vehicle对象初始化
- VehicleSurrogate&operator=(constVehicleSurrogate&VS)//重载赋值
- {
- if(this!=&VS)
- {
- deletethis->vp;
- this->vp=VS.vp?VS.vp->copy():0;
- }
- return*this;
- }
- ~VehicleSurrogate()
- {
- deletevp;
- }
- //代理Vehicle类行为的函数,Vehicle有多少个行为函数,这里就需要重新定义多少个代理类函数
- doubleget_weight()const
- {
- returnvp->get_weight();
- }
- private:
- Vehicle*vp;
- };
- VehicleSurrogateparking_lot[100];
- RoadVehiclex;
- parking_lot[0]=x;
- cout<<parking_lot[0].get_weight();
至此,我们利用代理类,就可以即不用显示进行内存分配管理,又可以使得Vehicle子类对象进行动态绑定。
2. 智能指针
“c++代理类(一)”中完成的简单代理类虽然解决了最急迫的问题,但效率上又存在了另外的问题,该简单类存在的问题主要是:
每个代理类对象都唯一关联一个实际对象,代理类对象存在则实际对象存在,代理类对象释放则实际类对象也要释放,且复制代理类对象就必须要复制实际类对象。这在实际类很大的时候复制开销是非常大的。而且,代理类的复制会频繁的发生,比如:作为函数的参数进行值传递,或者作为函数的返回值等等。
我们将对该简单代理类进行改进,改进的思想主要是:在代理类中为其代理的实际对象添加一个标记,该标记指出有多少个代理类对象代理了这个实际对象,这样当我们复制代理类对象时,其实际所代理的对象就不需要复制了,只需要修改该标记即可。
其具体做法如下:
- classVehicleSurrogate
- {
- public:
- ......//跟简单类一样,唯一不同的是需要加入处理标记num的部分
- private:
- Vehicle*vp;
- int*num;//添加的标记字段
- };
上述的策略在不需要修改实际对象时非常有用,即所有代理类对象只是读它所代理的实际对象时,但当某个代理类需要修改它所代理的实际对象时,问题就发生了,由于所有代理类对象实际所代理的对象在内存中是同一份,因此,一个代理类对象所做的修改将会影响其他代理类,因此,此时需要对所代理的实际对象进行复制,且该复制是无法避免的。我们称之为——写时复制。
实现指针的类代码如下:
- #include<iostream>
- usingnamespacestd;
- classVehicle
- {
- public:
- Vehicle():weight(0.0){}//默认构造函数
- Vehicle(doublew):weight(w){}
- virtual~Vehicle(){}//虚析构函数必须存在,因此所有子类对象在析构时都是以Vehicle*的方式调用析构函数的,以虚析构函数调用才能保证调用到正确的析构函数//才不会导致内存泄露
- virtualVehicle*copy()const//复制自己
- {
- returnnewVehicle(*this);
- }
- //读
- doubleget_weight()const
- {
- returnweight;
- }
- //写
- Vehicle*set_weight(doublew)
- {
- weight=w;
- returnthis;
- }
- private:
- doubleweight;
- };
- //定义陆地上的车辆
- classRoadVehicle:publicVehicle
- {
- public:
- RoadVehicle(){}//默认构造函数,容许声明RoadVehicle的数组
- RoadVehicle(doublew):Vehicle(w){}
- RoadVehicle(constRoadVehicle&RV):Vehicle(RV.get_weight()){}//拷贝构造函数
- Vehicle*copy()const//复制自己
- {
- returnnewRoadVehicle(*this);
- }
- ~RoadVehicle(){}
- };
- //定义飞机
- classAirCraft:publicVehicle
- {
- public:
- AirCraft(){}//默认构造函数,容许声明AirCraft的数组
- AirCraft(doublew):Vehicle(w){}
- AirCraft(constAirCraft&AC):Vehicle(AC.get_weight()){}//拷贝构造函数
- Vehicle*copy()const
- {
- returnnewAirCraft(*this);
- }
- ~AirCraft(){}
- };
- //智能指针类定义
- classVehicleSurrogate
- {
- public:
- VehicleSurrogate():vp(newVehicle()),num(newint(1)){}//默认构造函数,使得可以声明VehicleSurrogate的数组
- VehicleSurrogate(constVehicleSurrogate&VS):vp(VS.vp),num(VS.num)//拷贝构造函数,可发现此时它所代理的实际对象并未复制
- {
- ++(*num);
- }
- VehicleSurrogate(constVehicle&V):vp(V.copy()),num(newint(1)){}//以它所代理的Vehicle对象初始化
- VehicleSurrogate&operator=(constVehicleSurrogate&VS)//重载赋值,可发现此时它所代理的实际对象并未复制
- {
- if(this!=&VS)
- {
- //删除原来的旧的关联对象
- if(--(*num)==0)
- {
- deletevp;
- deletenum;
- }
- //赋值新的关联对象
- vp=VS.vp;
- num=VS.num;
- ++(*num);
- }
- return*this;
- }
- ~VehicleSurrogate()
- {
- if(--(*num)==0)
- {
- deletevp;
- deletenum;
- }
- }
- intget_num()const
- {
- return*num;
- }
- //代理Vehicle类行为的函数,读操作无需复制所代理的实际对象
- doubleget_weight()const
- {
- returnvp->get_weight();
- }
- //写时复制策略,写时必须复制所代理的实际对象
- VehicleSurrogate&set_weight(doublew)
- {
- if((*num)==1)
- {
- vp->set_weight(w);
- }
- else
- {
- --(*num);
- vp=vp->copy();//真正的复制发生在这里
- num=newint(1);
- vp->set_weight(w);
- }
- return*this;
- }
- private:
- Vehicle*vp;
- int*num;
- };
- intmain()
- {
- //测试上述智能指针
- VehicleSurrogateparking_lot[100];
- RoadVehiclex(10);
- parking_lot[0]=RoadVehicle(x);
- parking_lot[1]=parking_lot[0];
- parking_lot[0].set_weight(5.0);
- cout<<parking_lot[0].get_weight()<<endl<<parking_lot[0].get_num()<<endl;
- cout<<parking_lot[1].get_weight()<<endl<<parking_lot[1].get_num()<<endl;
- }
使用智能指针,既保留了简单代理类的优点:无需显示管理内存分配,且能实现所代理的实际对象动态绑定,又省略了过多的复制开销。 转载:
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/