虚复制函数:
\N
\N 让我们想一个办法来复制编译时类型型未知的对象.我们知道,C++中处理未知类型的对象的方法就是使用虚函数.这种函数的一个显而易见的名字就是copy,clone也可以,不过稍微有点似是而非.
\N
\N 由于我们是想能够复制任何类型的Vehicle,所以应该在Vehicle类中增加一个合适的纯虚函数:
\Nclass Vehicle{
\Npublic:
\Nvirtual double weight ( ) const =0 ;
\Nvirtual void start ( ) = 0 ;
\Nvirtual Vehicle* copy() const = 0;
\N};
\N
\N 接下来,在每个派生自Vehicle的类中添加一个新的成员函数copy.指导思想就是,如果vp的指向某个继承自Vehicle的不确定类的对象,则vp->copy()会获得一个指针,该指针指向该对象的一个新建的副本.例如,如果Truck继承类Vehicle,则它的copy函数就类似于:
\N Vehicle* Truck::copy() const
\N{
\N Return new Truck(*this);
\N}
\N
\N当然,处理完一个对象后,需要清除该对象.要做到这一点,就必须确保类Vehicle有一个虚析构函数.
\N
\N
\N
\N定义类:
\N
\N我们已经理解了根据需要复制对象的方法.现在,来看看内存分配.有没有一种方法既能使我们避免显式地处理内存分配,又能保持类Vehicle在运行时绑定的属性呢?
\N
\N解决这个问题的关键是要用类来表示概念,这在C++中是很常见的.我总是把这一点当作最基本的C++设计原则.在复制对象的过程中运用这个设计原则,就晃定义一个行为和Vehicle对象相似.而又潜在地表示了所有继承自Vehicle类的对象的东西.我们把这种类的对象叫做.
\N
\N每个Vehicle都代表某个继承自Vehicle类的对象,只要该关联着这个对象,该对象就肯定存在.因此,复制就会复制相对应的对象,而给赋值也会先删除旧对象.再复制新对象.
\NClass VehicleSurrogate
\N{
\NPublic:
\N VehicleSurrogate();
\NVehicleSurrogate(const Vehicle&);
\N~VehicleSurrogate();
\NVehicleSurrogate(const VehicleSurrogate&);
\NVehicleSurrogate& operator=(const VehicleSurrogate&);
\NPrivate:
\NVehicle* vp;
\N};
\N
\N上述类有一个以const Vehicle&为参数的构造函数,这样就能为任意继承自Vehicle的类的对象创建了.同时,类还有一个缺省构造函数,所以我们能够创建VehicleSurrogate对象的数组.
\N
\N然而,缺省构造函数也给我们带来了问题:如果Vehicle是个抽象基类,我们应该如果规定VehicleSurrogate的缺省操作?它所指向的对象的类型是什么?不可能是vehicle,因为根本就没有vehicle对象.
\N
\N为了得到一个更好的方法,我们要引入行为类似于零指针的空的概念.能够创建.销毁和复制这样的,但是进行其他操作就视为出错.
\N
\N到目前为止,不再需要任何其他的操作了,这就使我们能很容易地写出成员函数的定义:
\N
\NVehicleSurrogate:: VehicleSurrogate():vp(0){ }
\NVehicleSurrogate:: VehicleSurrogate(const Vehicle& v):vp(v.copy()){ }
\NVehicleSurrogate:: ~VehicleSurrogate()
\N{
\N delete vp;
\N}
\NVehicleSurrogate:: VehicleSurrogate(const VehicleSurrogate& v)
\N:vp(v.vp? v.vp->copy() : 0 ) { }
\NVehicleSurrogate:: operator=(const VehicleSurrogate& v)
\N{
\N If(this!=&v)
\N{
\N delete vp;
\N vp=(v.vp? v.vp->copy() : 0 );
\N}
\NReturn * this;
\N}
\N这里有3个技巧值得我们注意.
\N
\N首先,注意每次对copy的调用都是一个虚拟设用.这些调用只能是虚拟的,别无选择,因为类vehicle的对象不存在.即使是那个只接收一个const vehicle&参数的复制构造函数中,它所进行的v.copy调用也是一次虚拟调用,因为v是一个引用而不是一个普通对象.
\N
\N其次,注意前于复制构造函数和赋值操作符中的v.vp非零的检测,这个检测是必需的,否则调用v..vp->copy时就会出错.
\N
\N再次,注意对赋值操作符进行检测,确保没有将赋值经它自身.
\N
\N下面剩下的工作只是令该类支持类vehicle所能支持的其它操作了.在前面的例子中,有weiht和start,所以要把它们加入到类VehicleSurrogate中:
\NClass VehicleSurrogate
\N{
\NPublic:
\N VehicleSurrogate();
\NVehicleSurrogate(const Vehicle&);
\N~VehicleSurrogate();
\NVehicleSurrogate(const VehicleSurrogate&);
\NVehicleSurrogate& operator=(const VehicleSurrogate&);
\NDouble weight() const;
\NVoid start();
\NPrivate:
\NVehicle* vp;
\N};
\N
\N注意这些函数都不是虚拟的,我们这里所使用的对象都是类VehicleSurrogater 的对象,没有继承自该类的对象.当然,函数本身可以调用相应vehicle对象中的虚函数.它们也应该检查确保vp不为零.
\N
\N
\N
\NDouble VehicleSurrogate::weight() const
\N{
\N If(vp= =0)
\N Thorw *empty VehicleSurrogate.weight()*;
\N Return vp->weight();
\N}
\N
\Nvoid VehicleSurrogate::start()
\N{
\N If(vp= =0)
\N Thorw *empty VehicleSurrogate.start()*;
\N Return vp->start();
\N}
\N一旦我们完成了所有这些工作,就很容易定义我们的停车场(parking_lot)了:
\NVehicleSurrogate parking_lot[1000];
\NAutomobile x;
\NParking_lot[num_vehicles++]=x;
\N最后一条语句等价于:
\NParking_lot[num_vehicles++] = VehicleSurrogate(x);
\N
\N这个语句创建了一个关于对象x的副本,并将VehicleSurrogate对象绑定到该副本,然后将这个对象赋值给parking_lot的一个元素.当最后销毁parking_lot数组时,所有这些副本也将被清除.
\N
\N小结:
\N
将继承和容器共用,迫使我们要处理两个问题:控制内存分配和把不同类型的对象放入同一个容器中.采用基础的C++技术,用类来表示概念,我们可以同时兼顾这两个问题..做这些事情的时候,我们提出一个名叫类的类,这个类的每个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象.通过在容器中用对象而不是对象本身的方式,解决了我们的问题.