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

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

在 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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值