派生一个类
class RatedPlayer : public TableTennisPlayer
{};
使用公有派生,基类的公有成员将成为派生类的公有成员,基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。
创建派生类前首先创建基类
Ratedplayer::RatedPlayer(usigned int r , const string & fn , const string & ln , bool ht):TableTennisPlayer
(fn , ln , ht)
{
rating = r;
}
如果没有基类初始化表则调用基类默认构造函数,如果传入的参数是基类的引用,则调用基类复制构造函数
派生类和基类的特殊关系
(1)派生类可以使用基类的非私有方法。
(2)基类指针可以在不进行显示类型的转换的情况下指向派生类对象,基类引用可以在不进行显示类型转换的情况下引用派生类对象
但是,基类指针和引用只能调用基类方法
对于基类指针/引用,可以用派生类对象赋值,这样会调用隐式值构造函数或重载赋值运算符
继承关系
公有继承通常是一种is-a关系,即派生类对象也是一个基类对象,可以对基类对象执行的操作也能对派生类对象执行
多态公有继承
派生类想改变基类的方法
(1)在派生类中重新定义基类的方法
(2)使用虚方法
使用虚方法只在类声明中加入关键字virtual
如果创建了基类指针数组如下:
Brass * P_clients[CLIENTS];
指针既可以指向基类对象又可以指向基类对象又可以指向派生类对象
如果指向的是基类Brass对象,则调用Brass::ViewAcct(),如果指向的是BrassPlus派生类对象,则调用BrassPlus::ViewAcct()
如果Brass::ViewAcct()被声明为虚的,则任何情况都调用Brass::ViewAcct()
静态联编和动态联编
联编:将函数调用解释成特定的函数代码块。
在编译过程进行联编称为静态联编,但是有虚函数的时候使用哪个函数不能在编译时确定,所以编译器必须生成能够在程序运行时选择正确的虚方法的代码,称为动态联编。
可以将基类指针指向派生类对象(称为向上强制转换)
不能隐式地将派生类指针指向基类对象(称为向下强制转换)
Brass b;
BrassPlus bp;
Brass * b1 = &bp;//允许向上隐式转换
BrassPlus *bp1 = (BrassPlus *) &b;
对于使用基类引用或指针作为参数的函数调用,将进行向上转换
void fr(Brass & rb);
void fp(Brass * pb);
void fv(Brass b);
int main()
{
Brass b("Billy Bee",123,123);
BrassPlus bp("Betty Beep",456,456);
fr(b);//调用Brass::ViewAcct()
fr(bp);//调用BrassPlus::ViewAcct()
fp(b);//调用Brass
fp(bp);//调用BrassPlus
fv(b);//调用Brass
fv(bp);//调用Brass
...
}
按值传递导致只将BrassPlus对象的Brass部分传给fv()
虚成员和动态联编
有时候静态联编效率高,如派生类不定义基类的方法
虚函数工作原理
编译器处理虚函数的方法是:给每个对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针。这种数组称为虚函数表。虚函数表中存储了为类对象进行声明的虚函数的地址。
例如:基类对象包含一个指针,该指针指向基类中所有虚函数的地址表。派生类对象中将包含一个指向独立地址表的指针。如果派生类提供虚函数的新定义,该虚函数表将保存新函数的地址。如果派生类没有重新定义虚函数,该表将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该地址被添加到表中。
使用虚函数的成本
(1)每个对象都将增大,增大量为存储地址的空间
(2)对于每个类,编译器都创建一个虚函数地址(数组)
(3)对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址
注意事项
(1)构造函数不能是虚函数
(2)析构函数应该是虚函数
(3)友元不能是虚函数,因为友元不是类成员
(4)重新定义将隐藏基类的方法
如果基类声明被重载了,则应在派生类中重新定义所有的基类版本
protected
private和protected的区别只有在基类派生的类中才会表现出来。派生类可以直接访问基类的保护成员,但不能直接访问基类的私有成员。对于外部世界来说,保护对象的行为和私有对象一样。
抽象基类
纯虚函数
class BaseEllipse
{
private:
double x;
double y;
....
public:
BaseEllipse(double x0 = 0,double y0 = 0) : x(x0),y(y0) {}
virtual double Area() const = 0;
...
}
当类声明中包含纯虚函数时,则不能创建该类的对象。原型中的=0使虚函数称为纯虚函数。
原型中使用=0指出类是一个抽象基类,在类中可以不定义该函数。但是可以在实现文件中提供方法的定义。
继承和动态内存分配
(1)派生类不使用new
不需要为派生类定义显示析构函数 复制构造函数和赋值运算符
(2)派生类使用new
必须为派生类定义显示析构函数 复制构造函数和赋值运算符
P523-P530不赘述,都是重点