原书抄录:
组件之间的交互关系更少并且更简单,将使得程序更容易理解和维护。(低耦合,高内聚)
当我们要决定在一个类中到底是使用数据成员还是函数成员来表示一个特性时,我们首先应该考虑:
这个特性是用属性值来描述的还是由用行为来描述的?- 如果特性是一个属性值,那么用数据成员来表示就更简单。派生类对象将使用这个数据成员,并且可使用它的值。
- 如果这个特性是一种行为(一种操作或者算法),那么我们应该用成员函数来表示它。这个成员函数可以是虚函数,也可以是非虚函数。
我们还要进一步提出问题:
派生类和基类是否有着同样的行为?- 派生类必须表现出不同的行为,那么就应该使用虚函数,这样每个派生类都给出自己的实现。
- 如果所有的派生类都表现出相同的行为,那么他们就可以共享一个在基类中定义的非虚函数。
使用虚函数的开销是难以确定的 。包含虚函数的类中存在虚指针,指向一个描述虚函数的表。
规则总结:
- 派生类在处理继承而来的状态时必须与基类保持一致。(一致性)
- 如果在公有基类中没有定义虚析构函数,那么在所有的派生类或者派生类的数据成员中都应该没有定义虚析构函数。
- 通常情况下,公有基类的析构函数应该被声明为虚函数。
- 将公有的行为迁移到基类中。
- 降低耦合性 —— 将类之间的交互最小化。
- 如果派生类之间的区别在于属性,则用数据成员来表示,如果在于行为,则用虚函数来表示。
- 没有哪个类是完美的;过窄的设计要好于过宽的设计。
- 我们应该考虑使用默认参数的形式来代替函数重载的形式。
章后习题:
很简单,题目就不写了。
答案:是因为group是直接赋值得到的,没有申请内存,自然也不需要删除了。
最后附上本章中研究的代码前后对比。
最初的代码(略有修改):
#include <iostream>
#include <cstring>
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 identify()
{
std::cout << "generic vehicle" << std::endl;
}
};
class Car: public Vehicle{
public:
Car(): Vehicle() {}
Car(char *p): Vehicle(p) {}
void identify()
{
std::cout << "car with plate " << plate << std::endl;
}
};
class Truck: public Vehicle{
public:
Truck(): Vehicle() {}
Truck(char *p): Vehicle(p) {}
void identify()
{
const char *p = plate ? plate : "<none>";
std::cout << "truck with plate " << p << std::endl;
}
};
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]){
std::cout << "Vehicle int bay " << bay << " is :";
parked[bay]->identify();
}
}
}
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("IKY 707");
Truck t3("FFY 808");
Truck t4("PLS 909");
Truck t5("SLY 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;
}
经过作者(当然是书的作者了)的分析总结修改,最后的代码(运用这些规则后):
#include <iostream>
#include <cstring>
class Vehicle
{
protected:
char *plate;
char *group;
public:
Vehicle(char *g, char *p);
virtual ~Vehicle() = 0;
void identify();
};
Vehicle::Vehicle(char *g, char *p)
{
group = g;
if(p){
plate = new char[strlen(p) + 1];
strcpy(plate, p);
}
else{
plate = NULL;
}
}
Vehicle::~Vehicle()
{
if(plate){
delete [] plate;
}
}
void Vehicle::identify()
{
const char *p = plate ? plate : "<none>";
std::cout << group << " with plate " << p << std::endl;
}
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]){
std::cout << "Vehicle int bay " << bay << " is :";
parked[bay]->identify();
}
}
}
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("IKY 707");
Truck t3("FFY 808");
Truck t4("PLS 909");
Truck t5("SLY 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;
}