一起学习C++中的面向对象编程-第14章

xiaoxiao2021-02-28  138

多态只用于通过继承相关联类型的引用或指针上。

在 C++ 中,基类必须指出希望派生类重写哪些函数,定义为 virtual 的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数

注意:继承层次的根类一般都要定义虚析构函数.保留字virtual 的目的是启用动态绑定。成员默认为非虚函数,对非虚函数的调用在编译时确定。为了指明函数为虚函数,在其返回类型前面加上保留字 virtual。除了构造函数之外(构造函数不能定义为虚函数),任意非 static 成员函数都可以是虚函数。保留字只在类内部的成员函数声明中出现,不能用在类定义体外部出现的函数定义上。(对构造函数和static修饰的成员函数不能作为虚函数,不能用virtual修饰)

#include <iostream>using namespace std;class Item_base{public: Item_base(const string& book = " ",double sales_price = 0.0):isbn(book),price(sales_price) { } string book() const { return isbn; } virtual double net_price(size_t n) const { return n* price; } virtual ~Item_base() { }private: string isbn;protected: double price;};class Bulk_item:public Item_base{public: Bulk_item(size_t num=9,double discount=0.2):Item_base("lizhi",13),min_qty(num),discount(discount){} double net_price(size_t n) const;//这里的vitual可以省略,也可以不省略private: size_t min_qty; double discount;};double Bulk_item::net_price(size_t n) const{ if(n >= min_qty) return n*(1-discount) * price; else return n*price;}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;}int main(int argc, char *argv[]){ Item_base base("u32oiuo3",12); Bulk_item derived; print_total(cout,base,10); print_total(cout,derived,20); return 0;}

派生类对其基类类型对象的 protected 成员没有特殊访问权限,对于基类中的protected成员,派生类中可以直接调用,在派生类内部通过派生类对象来调用,但不能通过基类对象来间接调用。

void Bulk_item::memfcn(const Bulk_item &d, const Item_base &b) { // attempt to use protected member double ret = price; // ok: uses this->price ret = d.price; // ok: uses price from a Bulk_item object ret = b.price; // error: no access to price from an Item_base }

上面的函数定义在派生类的内部,所以在内的类的内部可以使用使用类,即类的使用用户可以是类的成员,也可以是类的普通用户(类外)。

尽管不是必须这样做,派生类一般会重定义所继承的虚函数。派生类没有重定义某个虚函数,则使用基类中定义的版本

一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类 无法改变该函数为虚函数这一事实。派生类重定义虚函数时, 可以使用 virtual 保留字,但不是必须这样做

从效果来说,最底层的派生类对象包含其每个直接基类和间接基类的子对象。

用作基类的类必须是已定义的,。这一规则暗示着不可能从类自身派生出一个类

派生类的声明:

如果需要声明(但并不实现)一个派生类,则声明包含类名但不包含派生列 表。例如,下面的前向声明会导致编译时错误: // error: a forward declaration must not include the derivation list class Bulk_item : public Item_base; 正确的前向声明为: // forward declarations of both derived and nonderived class class Bulk_item; class Item_base;

多态的实现两个因素:

第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;第二,必须通过基类类型的引用或指针进行函数调用

因为可以使用基类类型的指针或引用来引用派生类型对象,所以,使用基类类型的引用或指针时,不知道指针或引用所绑定的对象的类型:基类类型的引用 或指针可以引用基类类型对象,也可以引用派生类型对象。无论实际对象具有哪种类型,编译器都将它当作基类类型对象。将派生类对象当作基类对象是安全的, 因为每个派生类对象都拥有基类子对象。而且,派生类继承基类的操作,即,任何可以在基类对象上执行的操作也可以通过派生类对象使用

通过引用或指针调用虚函数时,编译器将生成代码,在运行时确定调用哪个 函数,被调用的是与动态类型相对应的函数。例如,我们再来看 print_total 函 数: // calculate and print price for given number of copies, applying any discounts

void print_total(ostream &os, const Item_base &item, size_t n) { os << "ISBN: " << item.book() // calls Item_base::book << "\tnumber sold: " << n << "\ttotal price: " // virtual call: which version of net_price to call is resolved at run time << item.net_price(n) << endl; } 因为 item 形参是一个引用且 net_price 是虚函数,item.net_price(n) 所调 用的 net_price 版本取决于在运行时绑定到 item 形参的实参类型

Item_base base; Bulk_item derived; // print_total makes a virtual call to net_price print_total(cout, base, 10); // calls Item_base::net_price print_total(cout, derived, 10); // calls Bulk_item::net_price

转载请注明原文地址: https://www.6miu.com/read-41817.html

最新回复(0)