1.例子
设计一个车辆管理程序,程序对车辆进入车库与离开车库进行记录。
2.代码
#include #include class Vehicle { protected: char* plate; public: Vehicle() { plate = NULL;} Vehicle(char* p) { plate = new char[strlen(p) + 1]; strcpy(plate,p); } ~Vehicle() {delete [] plate;} virtual void identfy() { printf("generic vehicle/n"); } }; class Car : public Vehicle { public: Car() : Vehicle() {} Car(char* p) : Vehicle(p) {} void identfy() { char *p = plate ? plate : ""; printf("car with plate %s/n",p); } }; class Truck : public Vehicle { public: Truck() : Vehicle() {} Truck(char *p):Vehicle(p){} void identfy() { char *p = plate ? plate : ""; printf("car with plate %s/n",p); } }; class Garage { int maxVehicles; Vehicle **parked; public: Garage(int max); ~Garage(); int accept(Vehicle*); Vehicle *release(int bay); void listVehicles(); }; Garage::Garage(int max) { maxVehicles = max; parked = new Vehicle*[maxVehicles]; for(int bay = 0;bay < maxVehicles;++bay) parked[bay] = NULL; } Garage::~Garage() { delete [] parked; } int Garage::accept(Vehicle *veh) { for(int bay = 0;bay < maxVehicles;++bay) if(!parked[bay]) { parked[bay] = veh; return bay; } return -1; } Vehicle *Garage::release(int bay) { if(bay < 0 || bay >= maxVehicles) return NULL; Vehicle *veh = parked[bay]; parked[bay] = NULL; return veh; } void Garage::listVehicles() { for(int bay = 0;bay < maxVehicles;++bay) if(parked[bay]) { printf("Vehicle in bay %d is: ",bay); parked[bay]->identfy(); } } Car c1("RVR 101"); Car c2("SPT 202"); Car c3("CHP 303"); Car c4("BDY 404"); Car c5("BCH 505"); Truck t1("TBL 606"); Truck t2("TBL 707"); Truck t3("TBL 808"); Truck t4("TBL 909"); Truck t5("TBL 000"); int main() { Garage Park(15); Park.accept(&c1); int t2bay = Park.accept(&t2); Park.accept(&c3); Park.accept(&t1); int c4bay = Park.accept(&c4); Park.accept(&c5); Park.accept(&t5); Park.release(t2bay); Park.accept(&t4); Park.accept(&t3); Park.release(c4bay); Park.accept(&c2); Park.listVehicles(); return 0; }
3.代码分析
3.1 析构函数
如果定义一个
Vehicle *p = new car //.... delete p
程序将在编译器更具delete表达式中指针的类型来决定调用哪个析构函数,由于p是基类型指针,并且基类的析构函数是非虚的,因此只有基类的析构函数被执行,如果有涉及到派生类对象的析构函数的内容,程序将会出错。
原则:
a.如果在公有基类中没有定义虚析构函数,那么在所有的派生类或者派生类的数据成员中都应该没有定义析构函数。
b.通常情况下,公有基类的析构函数应该被声明为虚函数。
3.2 继承
由于Car 和Truck的唯一区别就是属性值,一个字符串的名称不同而已,二者在行为上没有区别;考虑到这一点,可以将共同行为identfy()迁移到基类中去。由于这一迁移,基中原来的protected的plate数据成员无需在其派生类中被访问到,因而可以将其改为private.
原则:
c.将共同行为迁移到基类中。
经过这两步后的程序为:
#include #include class Vehicle { private: char* plate; virtual char *group() = 0; public: Vehicle() { plate = NULL;} Vehicle(char* p) { plate = new char[strlen(p) + 1]; strcpy(plate,p); } virtual ~Vehicle() {delete [] plate;} void identfy() { char *p = plate ? plate : ""; printf("%s with plate %s/n",group(),p); } }; class Car : public Vehicle { private: char *group() {return "car";} public: Car() : Vehicle() {} Car(char* p) : Vehicle(p) {} }; class Truck : public Vehicle { private: char *group() {return "truck";}//成员函数的作用:作为类的实现 public: Truck() : Vehicle() {} Truck(char *p):Vehicle(p){} }; class Garage { int maxVehicles; Vehicle **parked; public: Garage(int max); ~Garage(); int accept(Vehicle*); Vehicle *release(int bay); void listVehicles(); }; Garage::Garage(int max) { maxVehicles = max; parked = new Vehicle*[maxVehicles]; for(int bay = 0;bay < maxVehicles;++bay) parked[bay] = NULL; } Garage::~Garage() { delete [] parked; } int Garage::accept(Vehicle *veh) { for(int bay = 0;bay < maxVehicles;++bay) if(!parked[bay]) { parked[bay] = veh; return bay; } return -1; } Vehicle *Garage::release(int bay) { if(bay < 0 || bay >= maxVehicles) return NULL; Vehicle *veh = parked[bay]; parked[bay] = NULL; return veh; } void Garage::listVehicles() { for(int bay = 0;bay < maxVehicles;++bay) if(parked[bay]) { printf("Vehicle in bay %d is: ",bay); parked[bay]->identfy(); } } Car c1("RVR 101"); Car c2("SPT 202"); Car c3("CHP 303"); Car c4("BDY 404"); Car c5("BCH 505"); Truck t1("TBL 606"); Truck t2("TBL 707"); Truck t3("TBL 808"); Truck t4("TBL 909"); Truck t5("TBL 000"); int main() { Garage Park(15); Park.accept(&c1); int t2bay = Park.accept(&t2); Park.accept(&c3); Park.accept(&t1); int c4bay = Park.accept(&c4); Park.accept(&c5); Park.accept(&t5); Park.release(t2bay); Park.accept(&t4); Park.accept(&t3); Park.release(c4bay); Park.accept(&c2); Park.listVehicles(); return 0; }
3.3耦合(coupling)
group()的使用减少了Vehicle与派生类之间的耦合。所谓耦合就是指组件之间的相互作用程度。最初的程序中派生类通过三种方式与基类进行交互:首先,派生类的构造函数需要为基类的构造函数提供一个参数。其次,派生类中需要定义自己的identfy()函数。最后,派生类的identfy()需要访问基类的plate成员值。修改后,通过group()这种方式代替了后两种交互。降低了耦合。
现在考虑第一种:数据成员的值可以通过Vehicle的构造函数增加一个参数来获得,并且这个参数值将有相应的派生类的构造函数获得。
原则:
d.降低耦合性,将类之间的交互最小化。
e.如果派生类之间的区别在于属性,则用数据成员来表示;如果在于行为,则用虚函数来表示。(group表示的其实是一种属性)
经过这步后:
#include #include class Vehicle { private: char* plate; char *group; public: Vehicle(char *g) { group = g;plate = NULL;} Vehicle(char* g,char *p) { group = g; plate = new char[strlen(p) + 1]; strcpy(plate,p); } virtual ~Vehicle() {delete [] plate;} void identfy() { char *p = plate ? plate : ""; printf("%s with plate %s/n",group,p); } }; class Car : public Vehicle { public: Car() : Vehicle("car") {} Car(char* p) : Vehicle("car",p) {} }; class Truck : public Vehicle { public: Truck() : Vehicle("truck") {} Truck(char *p):Vehicle("truck",p){} }; class Garage { int maxVehicles; Vehicle **parked; public: Garage(int max); ~Garage(); int accept(Vehicle*); Vehicle *release(int bay); void listVehicles(); }; Garage::Garage(int max) { maxVehicles = max; parked = new Vehicle*[maxVehicles]; for(int bay = 0;bay < maxVehicles;++bay) parked[bay] = NULL; } Garage::~Garage() { delete [] parked; } int Garage::accept(Vehicle *veh) { for(int bay = 0;bay < maxVehicles;++bay) if(!parked[bay]) { parked[bay] = veh; return bay; } return -1; } Vehicle *Garage::release(int bay) { if(bay < 0 || bay >= maxVehicles) return NULL; Vehicle *veh = parked[bay]; parked[bay] = NULL; return veh; } void Garage::listVehicles() { for(int bay = 0;bay < maxVehicles;++bay) if(parked[bay]) { printf("Vehicle in bay %d is: ",bay); parked[bay]->identfy(); } } Car c1("RVR 101"); Car c2("SPT 202"); Car c3("CHP 303"); Car c4("BDY 404"); Car c5("BCH 505"); Truck t1("TBL 606"); Truck t2("TBL 707"); Truck t3("TBL 808"); Truck t4("TBL 909"); Truck t5("TBL 000"); int main() { Garage Park(15); Park.accept(&c1); int t2bay = Park.accept(&t2); Park.accept(&c3); Park.accept(&t1); int c4bay = Park.accept(&c4); Park.accept(&c5); Park.accept(&t5); Park.release(t2bay); Park.accept(&t4); Park.accept(&t3); Park.release(c4bay); Park.accept(&c2); Park.listVehicles(); return 0; }
3.4 默认参数
在基类和派生类中各有两个构造函数,区别在于第二个参数,在只有一个参数的构造函数中,第二个参数被视为空的。因而我们考虑使用默认参数的形式来代替函数重载,这能够减少一半的构造函数,付出的代价是提高了基类与派生类之间的耦合性。
写在基类内部的成员函数被默认为内联的,在没有明显好处的情形下,应该放在基外。
原则:
f.考虑使用默认参数的形式来代替函数重载。
修改后的函数代码为:
#include #include class Vehicle { private: char* plate; char *group; public: Vehicle::Vehicle(char* g,char *p); virtual ~Vehicle() = 0; void identfy() { char *p = plate ? plate : ""; printf("%s with plate %s/n",group,p); } }; Vehicle::Vehicle(char* g,char *p) { group = g; if(p) { plate = new char[strlen(p) + 1]; strcpy(plate,p); } else plate = NULL; } Vehicle::~Vehicle() { delete [] plate; } class Car : public Vehicle { public: Car(char* p = NULL) : Vehicle("car",p) {} }; class Truck : public Vehicle { public: Truck(char *p = NULL):Vehicle("truck",p){} }; class Garage { int maxVehicles; Vehicle **parked; public: Garage(int max); ~Garage(); int accept(Vehicle*); Vehicle *release(int bay); void listVehicles(); }; Garage::Garage(int max) { maxVehicles = max; parked = new Vehicle*[maxVehicles]; for(int bay = 0;bay < maxVehicles;++bay) parked[bay] = NULL; } Garage::~Garage() { delete [] parked; } int Garage::accept(Vehicle *veh) { for(int bay = 0;bay < maxVehicles;++bay) if(!parked[bay]) { parked[bay] = veh; return bay; } return -1; } Vehicle *Garage::release(int bay) { if(bay < 0 || bay >= maxVehicles) return NULL; Vehicle *veh = parked[bay]; parked[bay] = NULL; return veh; } void Garage::listVehicles() { for(int bay = 0;bay < maxVehicles;++bay) if(parked[bay]) { printf("Vehicle in bay %d is: ",bay); parked[bay]->identfy(); } } Car c1("RVR 101"); Car c2("SPT 202"); Car c3("CHP 303"); Car c4("BDY 404"); Car c5("BCH 505"); Truck t1("TBL 606"); Truck t2("TBL 707"); Truck t3("TBL 808"); Truck t4("TBL 909"); Truck t5("TBL 000"); int main() { Garage Park(15); Park.accept(&c1); int t2bay = Park.accept(&t2); Park.accept(&c3); Park.accept(&t1); int c4bay = Park.accept(&c4); Park.accept(&c5); Park.accept(&t5); Park.release(t2bay); Park.accept(&t4); Park.accept(&t3); Park.release(c4bay); Park.accept(&c2); Park.listVehicles(); return 0; }