转自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()函数只调用一次。
从中得到的一点理会:我们在阅读技术书籍的时候,一定要多问一个为什么,一定要深挖下去,要保持
怀疑的态度--尽信书不如无书