面向对象编程基于三个基本概念:数据抽象(类)、继承(用类派生从一个类继承另一个)和动态绑定(使得编译器能够在运行时决定是使用基类中定义的函数还是使用派生类中定义的函数。
面向对象编程的关键思想是多态性(polymorphism)。多态性:在许多情况下可以互换地使用派生类型或基类型的“许多形态”。在c++中
多态性仅用于通过继承而相关联的类型的引用或指针。
在c++中,基类必须指出希望派生类重新写哪些函数,定义为virtual函数是基类期待派生类重新定义的函数,基类希望派生类继承的函数不能定义为虚函数。
动态绑定:我们能够编写程序使用继承层次中任意类型的对象,无须关心对象的具体类型。使用这些类的程序无须区分函数是在基类函数在派生类中定义的。
在c++中,通过基类的引用(或指针)调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象。用引用(或指针)调用的虚函数在运行时确定。
用作基类的类必须是已定义的
用派生类作为新的基类
virtual与其它函数
c++中的函数调用默认不使用动态绑定。要触发动态绑定,满足两个条件:
1。只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;
2。必须通过基类类型的引用或指针进行函数调用。
从派生类型到基类的转换
因为每个派生类对象都包含基类部分,所以可将基类类型的引用绑定到派生类对象的基类部分,也可以用指向基类的指针指向派生类对象。
double print_total(const Item_base&, size_t);
Item base_item; //object of base type
print_total(item,10);
Item_base *p = &item;
Bulk_item bulk; //object of derived type
print_total(bulk,10);
p = &bulk;
上面的代码使用同一基类类型指针指向基类类型的对象和派生类型的对象,该代码还传递基类类型和派生类型的对象来调用需要基类类型引用的函数,两种使用都是正确的,因为每个派生类对象都拥有基类部分。
因为可以使用基类类型的指针或引用来引用派生类型对象,所以,使用基类类型的引用或指针时,不知道指针或引用所绑定的对象的类型:基类类型的引用或指针可以引用基类类型对象,也可以引用派生类型对象。无论实际对象具有哪种类型,编译器都将它当做基类类型对象。
通过引用或指针调用虚函数时,编译器将生成代码,在运行时确定调用哪个函数,被调用的是与动态类型相对应的函数。
void print_total(ostream &os, const Item_base &item, size_t n)
{
os << "ISBN: " << item.book()
<< "/tnumber sold: "<< n <<"/ttotal price: "
<< item.net_price(n) << endl;
}
因为 item 形参是一个引用且 net_price 是虚函数,item.net_price(n) 所调
用的 net_price 版本取决于在运行时绑定到 item 形参的实参类型:
int main(int argc,char *argv[])
{
Item_base a;
Bulk_item b;
print_total(cout,a,10); // calls Item_base::net_price
print_total(cout,b,20); // calls Bulk_item::net_price
return 0;
}
在第一个调用中,item 形参在运行时绑定到 Item_base 类型的对象,因此,
print_total 内部调用 Item_base 中定义的 net_price 版本。在第二个调用
中,item 形参绑定到 Bulk_item 类型的对象,从 print_total 调用的是
Bulk_item 类定义的 net_price 版本。