派生类与基类
◆ 1、赋值兼容性在任何需要基类对象的地方都可以用公有派生类的对象来代替,这条规则称 赋值兼容规则 。它包括以下情况:
- 派生类的对象可以赋值给基类的对象,这时是把派生类对象中从对应基类中继承来的成员赋值给基类对象。反过来不行,因为派生类的新成员无值可赋。
- 可以将一个派生类的对象的地址赋给其基类的指针变量,但只能通过这个指针访问派生类中由基类继承来的成员,不能访问派生类中的新成员。同样也不能反过来做。
- 派生类对象可以初始化基类的引用。引用是别名,但这个别名只能包含派生类对象中的由基类继承来的成员。
◆ 2、应用举例
【例8.5】赋值兼容规则与自定义的复制构造函数。在【 例8.1 】中如果自定义复制构造函数,要利用赋值兼容规则。( 查看详细源代码 )
拷贝构造函数如下:
Person::Person(Person &ps)
{
strcpy(IdPerson,ps.IdPerson);
if(ps.Name!=NULL){
Name=new char[strlen(ps.Name)+1];
strcpy(Name,ps.Name);
}
else Name=NULL;
Sex=ps.Sex;
Birthday=ps.Birthday;
if(ps.HomeAddress!=NULL){
HomeAddress=new char[strlen(ps.HomeAddress)+1];
strcpy(HomeAddress,ps.HomeAddress);
}
else HomeAddress=NULL;
}
Student::Student(Student &Std):Person(Std){
//按赋值兼容规则Std可为Person实参
strcpy(NoStudent,Std.NoStudent);
for(int i=0;i<30;i++){
if(Std.cs[i].coursename!=NULL){
cs[i].coursename=new char[strlen(Std.cs[i].coursename)+1];
strcpy(cs[i].coursename,Std.cs[i].coursename);
}
else cs[i].coursename=NULL;
cs[i].grade=Std.cs[i].grade;
}
}
重载的拷贝赋值操作符如下:
Person & Person::operator=(Person &ps){
copy(ps); //基类“=”重载函数中加拷贝函数可方便派生类重载“=”
return *this;
}
void Person::copy(Person &ps)
{
strcpy(IdPerson,ps.IdPerson);
if(ps.Name!=NULL){
delete[]Name; //赋值要先清原来分配的空间
Name=new char[strlen(ps.Name)+1];
strcpy(Name,ps.Name);
}
else Name=NULL;
Sex=ps.Sex;
Birthday=ps.Birthday;
if(ps.HomeAddress!=NULL){
delete[ ]HomeAddress;//赋值要先清原来分配的空间,再重新分配
HomeAddress=new char[strlen(ps.HomeAddress)+1];
strcpy(HomeAddress,ps.HomeAddress);
}
else { delete[ ]HomeAddress;HomeAddress=NULL;}
}
Student & Student::operator=(Student &Std)
{
copy(Std);//按赋值兼容规则Std可为实参
strcpy(NoStudent,Std.NoStudent);
for(int i=0;i<30;i++){
if(Std.cs[i].coursename!=NULL){
delete [] cs[i].coursename;
//拷贝要先清原来分配空间,再重新分配
cs[i].coursename= new char[strlen(Std.cs[i].coursename)+1];
strcpy(cs[i].coursename,Std.cs[i].coursename);
}
else cs[i].coursename=NULL;
cs[i].grade=Std.cs[i].grade;
}
return *this;
}
深拷贝和浅拷贝由打印程序是看不出来的,不过浅拷贝具有潜在的错误。
继承与聚合
继承使派生类可以利用基类的成员,如果 把基类的对象作为一个新类的对象成员 ,也可以取得类似的效果,后者称为 聚合 。派生类采用继承方法。 公有派生时,派生类可以直接使用基类公有与保护成员,对这两种成员的使用和重新定义一个包括基类所有成员和派生类新增成员的全新的类没有区别,但不能直接使用基类私有成员。
成员对象是组合、聚合或嵌套的概念 ,使用成员对象中的成员,只能直接访问(对象名加点号加成员名)公有成员。在类的成员函数中不能直接访问和处理成员对象的私有和保护成员,而要通过成员对象的接口去间接访问和处理。 某些应用中,对象成员可以代替继承中的基类。
基类在派生类中只能继承一个(间接基类不在讨论之中)不能同时安排两个,否则成员名即使使用域分辨符也会发生冲突,例如:
class A{public:int K;...};
class B:public A,public A{...};
这是不容许的 ,类B中两个K无论如何无法分辨出来。但如果一定要用两个K只能采用成员对象:
class B{A a,b;....};
这样有两个K:a.K和b.K。 所以采用成员对象更加灵活。两者不是对立的,而是互为补充的。
派生类与模板
为了运行的效率,类模板是相互独立的,即独立设计,没有使用继承的思想。对类模板的扩展是采用 适配子 (adapter)来完成的。通用性是模板库的设计出发点之一,这是由泛型算法和函数对象等手段达到的。派生类的目标之一也是代码的复用和程序的通用性,最典型的就是MFC,派生类的优点是可以由简到繁,逐步深入,程序编制过程中可以充分利用前面的工作,一步步完成一个复杂的任务。
模板追求的是运行效率,而派生追求的是编程的效率。