转自http://blog.chinaunix.net/uid-25909722-id-2182249.html
《C++沉思录》是一本十分经典的C++参考书籍。虽然该书出版于1997年,但是,即使是现在读来,对学
习和掌握C++还是十分有价值的。一口气读到第五章《代理类》时,发现其中演示的例子似乎有不妥之处,可以进行一定的改进。
第五章讲的是为属于同一个继承体系的对象设计一个容器。该容器要能容纳该继承体系中所有类的对象。
其中用到了代理类的方法来实现。实现的原理是:为每一个对象利用复制构造函数生产一个复制品,然后
以该复制品为参数构造一个他的代理对象,然后将代理对象保存在容器中,也就间接的将复制品对象保存
在了容器中。书中的代码如下(进行了很小的一些无关痛痒的改动):
-
#include <iostream>
-
using std::cout;
- using std::endl;
-
-
class Vehicle {
-
public:
-
virtual ~Vehicle(){}
-
virtual double weight() const = 0;
-
virtual void start() = 0;
-
virtual Vehicle* copy() const = 0;
- // ...
-
protected:
-
double weights;
-
};
-
-
class RoadVehicle: public Vehicle
-
{
-
public:
-
Vehicle* copy() const{
-
cout << "RoadVehicle.copy()" << endl;
-
return new RoadVehicle(*this); //调用默认复制构造函数
-
}
-
virtual double weight() const { return
weights;}
- void start(){ cout << "RoadVehicle.start()" << endl; }
-
};
-
-
class AutoVehicle: public RoadVehicle
-
{
-
public:
-
Vehicle* copy()const{
-
cout << "AutoVehicle.copy()" << endl;
-
return new AutoVehicle(*this); //调用默认复制构造函数
-
}
-
void start(){ cout << "AutoVehicle.start()" << endl; }
-
};
-
-
class Aircraft: public Vehicle
-
{
-
public:
-
Vehicle* copy()const{
-
cout << "Aircraft.copy()" << endl;
-
return new Aircraft(*this); //调用默认复制构造函数
-
}
-
double weight() const { return
weights;}
-
void start(){ cout << "Aircraft.start()" << endl; }
-
};
-
-
class Helicopter: public Aircraft
-
{
-
public:
-
Vehicle* copy()const{
-
cout << "Helicopter.copy()" << endl;
-
return new Helicopter(*this); //调用默认复制构造函数
-
}
-
void start(){ cout << "Helicopter.start()" << endl; }
- };
-
/*
-
* Vehicle类的代理类VehicleSurrogate:
-
*/
-
class VehicleSurrogate
-
{
-
public:
-
VehicleSurrogate(): vp(0) {}
-
VehicleSurrogate(const Vehicle& v): vp(v.copy()) { }
-
~VehicleSurrogate(){ delete
vp; }
-
VehicleSurrogate(const VehicleSurrogate& v): vp(v.vp ? v.vp->copy() : 0) {}
-
VehicleSurrogate& operator=(const VehicleSurrogate& v){
-
if(&v != this){
-
delete vp;
-
vp = (v.vp ? v.vp->copy() : 0);
-
}
-
return *this;
- }
-
double weight() const{
-
if(vp == 0)
-
throw "empty VehicleSurrogate.weight()";
-
return vp->weight();
-
}
-
void start(){
-
if(vp == 0)
-
throw "empty VehicleSurrogate.start()";
-
vp->start();
-
}
- // ...
-
private:
-
Vehicle* vp;
-
};
-
-
int main()
-
{
-
VehicleSurrogate parking_lot[10];
-
RoadVehicle rv;
-
AutoVehicle av;
-
Aircraft airv;
-
Helicopter heli;
-
-
parking_lot[0] = rv;
-
parking_lot[1] = av;
-
parking_lot[2] = airv;
-
parking_lot[3] = heli;
-
-
parking_lot[0].start();
-
parking_lot[1].start();
-
parking_lot[2].start();
-
parking_lot[3].start();
-
-
return 0;
- }
运行:./vehicle
结果:
RoadVehicle.copy()
RoadVehicle.copy()
AutoVehicle.copy()
AutoVehicle.copy()
Aircraft.copy()
Aircraft.copy()
Helicopter.copy()
Helicopter.copy()
RoadVehicle.start()
AutoVehicle.start()
Aircraft.start()
Helicopter.start()
对于这个运行结果我们肯定会产生疑问:为什么对象的copy()函数会调用两次呢?
仔细分析代码不难看出:每一个这样的 parking_lot[0] = rv; 赋值操作其中隐含会调用的函数为:
1.先调用VehicleSurrogate::VehicleSurrogate(const Vehicle& v): vp(v.copy()) { }构造函数,
这里调用了一次copy()函数。通过该函数我们得到了一个类Vehicle的代理类VehicleSurrogate的对象。
2.对于等号=显然会调用VehicleSurrogate::
VehicleSurrogate& operator=(const VehicleSurrogate& v)赋值操作符函数,这里又调用了一次
copy()函数。所以一次赋值操作:parking_lot[0] = rv; 导致了两次copy()函数的调用。
导致的结果是:
在调用VehicleSurrogate::VehicleSurrogate(const Vehicle& v): vp(v.copy()) { }构造函数时
v.copy()调用的是:
-
Vehicle* RoadVehicle:: copy() const{
-
cout << "RoadVehicle.copy()" << endl;
-
return new RoadVehicle(*this); //调用默认复制构造函数
- }
在调用VehicleSurrogate& operator=(const VehicleSurrogate& v)赋值操作符函数时,其代码
如下:
-
VehicleSurrogate& operator=(const VehicleSurrogate& v){
-
if(&v != this){
-
delete vp;
-
vp = (v.vp ? v.vp->copy() : 0);
-
}
- return *this;
- }
的对象不是同一个对象,所以会执行: delete vp;将刚由类VehicleSurrogate的构造函数生产的对象
又释放掉,然后再一次执行:vp = (v.vp ? v.vp->copy() : 0); 实际上又调用了一次:
-
Vehicle* RoadVehicle:: copy() const{
-
cout << "RoadVehicle.copy()" << endl;
-
return new RoadVehicle(*this); //调用默认复制构造函数
- }
得到了RoadVehicle类的两个对象,进而又释放掉其中一个对象。
显然合理的过程应该是:只调用一次RoadVehicle类的复制构造函数,得到一个存储在堆上的对象
然后将代理类VehicleSurrogate的对象的vp指针指向该对象即可。
既然找到了原因,那么如何修改代码呢?
很简单我们只要在代理类VehicleSurrogate中增加一个成员函数即可:
-
void operator=(Vehicle& v){
-
vp = ((&v != 0)? v.copy() : 0);
- }
赋值操作符函数了,而是直接调用我们定义的函数:
void VehicleSurrogate::operator=(Vehicle& v)。这样就避免了两次调用copy()函数。
最终修改的代码如下:
-
#include <iostream>
-
using std::cout;
- using std::endl;
-
-
class Vehicle {
-
public:
-
virtual ~Vehicle(){}
-
virtual double weight() const = 0;
-
virtual void start() = 0;
-
virtual Vehicle* copy() const = 0;
- // ...
-
protected:
-
double weights;
-
};
-
-
class RoadVehicle: public Vehicle
-
{
-
public:
-
Vehicle* copy() const{
-
cout << "RoadVehicle.copy()" << endl;
-
return new RoadVehicle(*this); //调用默认复制构造函数
-
}
-
virtual double weight() const { return
weights;}
-
void start(){ cout << "RoadVehicle.start()" << endl; }
-
-
};
-
-
class AutoVehicle: public RoadVehicle
-
{
-
public:
-
Vehicle* copy()const{
-
cout << "AutoVehicle.copy()" << endl;
-
return new AutoVehicle(*this); //调用默认复制构造函数
-
}
-
void start(){ cout << "AutoVehicle.start()" << endl; }
-
};
-
-
class Aircraft: public Vehicle
-
{
-
public:
-
Vehicle* copy()const{
-
cout << "Aircraft.copy()" << endl;
-
return new Aircraft(*this); //调用默认复制构造函数
-
}
-
double weight() const { return
weights;}
-
void start(){ cout << "Aircraft.start()" << endl; }
-
};
-
-
class Helicopter: public Aircraft
-
{
-
public:
-
Vehicle* copy()const{
-
cout << "Helicopter.copy()" << endl;
-
return new Helicopter(*this); //调用默认复制构造函数
-
}
-
void start(){ cout << "Helicopter.start()" << endl; }
-
};
-
-
/*
-
* Vehicle类的代理类VehicleSurrogate:
-
*/
-
class VehicleSurrogate
-
{
-
public:
-
VehicleSurrogate(): vp(0) {}
-
VehicleSurrogate(const Vehicle& v): vp(v.copy()) { cout << "VehicleSurrogate(const
Vehicle& v)" << endl;}
-
~VehicleSurrogate(){ delete
vp; }
-
VehicleSurrogate(const VehicleSurrogate& v): vp(v.vp ? v.vp->copy() : 0) {}
-
VehicleSurrogate& operator=(const VehicleSurrogate& v){
-
if(&v != this){
-
cout << "VehicleSurrogate& operator=(const VehicleSurrogate& v)" << endl;
-
delete vp;
-
vp = (v.vp ? v.vp->copy() : 0);
-
}
-
return *this;
-
}
-
void operator=(Vehicle& v){
-
vp = ((&v != 0)? v.copy() : 0);
- }
-
double weight() const{
-
if(vp == 0)
-
throw "empty VehicleSurrogate.weight()";
-
return vp->weight();
-
}
-
void start(){
-
if(vp == 0)
-
throw "empty VehicleSurrogate.start()";
-
vp->start();
-
}
- // ...
-
private:
-
Vehicle* vp;
-
};
-
-
int main()
-
{
-
VehicleSurrogate parking_lot[10];
-
RoadVehicle rv;
-
AutoVehicle av;
-
Aircraft airv;
- Helicopter heli;
-
-
parking_lot[0] = rv;
-
parking_lot[1] = av;
-
parking_lot[2] = airv;
-
parking_lot[3] = heli;
-
-
parking_lot[0].start();
-
parking_lot[1].start();
-
parking_lot[2].start();
-
parking_lot[3].start();
-
-
return 0;
- }
运行:./vehicle
结果:
RoadVehicle.copy()
AutoVehicle.copy()
Aircraft.copy()
Helicopter.copy()
RoadVehicle.start()
AutoVehicle.start()
Aircraft.start()
Helicopter.start()
显然copy()函数只调用一次。
从中得到的一点理会:我们在阅读技术书籍的时候,一定要多问一个为什么,一定要深挖下去,要保持
怀疑的态度--尽信书不如无书
本文深入探讨并修正了经典C++参考书籍《C++沉思录》中关于代理类实现的问题,指出原始代码在对象赋值过程中导致了不必要的对象复制,并提出了解决方案以减少资源消耗和提高效率。

被折叠的 条评论
为什么被折叠?



