深度理解派生与多态

来源:互联网 发布:交换机的数据帖转发式 编辑:程序博客网 时间:2024/05/22 02:05

利用C++进行编程都了解,面向对象编程基于三个基本的概念:数据抽象、继承和动态绑定。在C++中,用类进行数据抽象,用类派生从一个类继承另外一个:派生类继承基类的成员。动态绑定使编译器能够在运行时决定是使用基类定义的函数还是派生类中定义的函数。

许多应用程序的特性可以用一些相关但略有不同的概念来描述。例如,书店可以用不同的书提供不同的定价策略,有些书只能按照给定价格出售,另一些书可以根据折扣策略出售。

面向对象编程(object-oriented programming,oop)与这种应用非常匹配。通过集成可以定义一些类型,以模拟不同种类的书,通过动态绑定编写程序,来实现这其中的差异!

1. 概述

面向对象编程的关键思想是多态性(polymorphism)。
继承是指通过继承我们能够定义这样的类,他们对类型之间的关系建模,共享公共的东西,仅仅特化本质上不同的东西。派生类(derived class)能够继承基类(base class)定义的成员,派生类可以无须改变而是用那些与派生类型具体特性不相关的操作,派生类可以无需改变而使用那些与派生类型具体特性不相关的操作,派生类可以重定义那些与派生类型相关的成员函数,将函数特化
实例:
#include <iostream>#include <string>using namespace std;class Item_Based{public:  Item_Based(const string &book,double sale_price){ isbn = book; price = sale_price;  }string book () const            { return isbn; }virtual double net_price(size_t n) const //???? the function of "const"{ return n * price; }private:string isbn;protected: double price;};class Bulk_Item: public Item_Based{public:Bulk_Item(const string &book ,double sale_price,      size_t qty,double dscnt)  :Item_Based(book,sale_price) //Based class Initialized{min_qty = qty; discount = dscnt;}virtual double net_price(size_t ) const;  //?????????private:size_t min_qty; // minimum purchase for discount to applydouble discount; // fractional discount to apply};inline double Bulk_Item::net_price(size_t  cnt) const{if (cnt >= min_qty){return (1-discount)*price*cnt;} else{return price*cnt;}}void print_total(ostream &os,Item_Based &item, size_t n){os << "ISBN: ";os << item.book(); //operate overloados << "\tnumber sold: " ;os << n << "\ttotal price: ";os << item.net_price(n);os <<endl;//why the part didn't do derived net_price}int main(){Item_Based base("DaoMuBiJi",30);Bulk_Item derived("LaoJiuMen",30,2,0.5);// print_total makes a virtual call to net_priceprint_total(cout,base,10); // calls Item_base::net_priceprint_total(cout,derived,10); // calls Bulk_item::net_price}
程序运行结果:
说明:1.虽然外接函数的第二个参数是Item_Base的引用,但是我们可以将Item_Base对象或者Bulk_Item对象传给它
           2.因为形参是引用且net_price是虚函数,所以对net_price的调用将在运行时确定。调用哪个版本的net_price将在运行时确定!调用哪个版本net_price将依赖于传给print_tatal的实参!如果传给print_total的实参是Bulk_Item对像,将运行Bluk_Item中定义的应用折扣net_price;如果实参是一个Item_Base对象,则调用Item_Base定义的版本。
核心:在C++中,通过基类的引用或指针调用虚函数时,将发生动态绑定。引用(或指针)即可以指向基类对象也可以指向派生类对象,这一个事实是动态绑定的关键!!!用引用或指针调用的虚函数在运行时确定。(需要强调一点的是:基类的虚函数与派生类的虚函数定义必须一模一样。。。由于在派生类中,我丢掉了const标识符,导致我一度的怀疑,动态绑定的可行性

2.访问情况

在基类中,public 和 private 标号具有普通含义:用户代码可以访问类的public 成员而不能访问 private 成员private 成员只能由基类的成员和友元访问派生类对基类的 public 和 private 成员的访问权限与程序中任意其他部分一样:它可以访问 public 成员而不能访问 private 成员
这里需要强调一点的是protected(受保护的访问标号)。protected成员可以被派生类对象访问,但不能被该类型的普通用户访问。
可以认为 protected 访问标号是 private 和 public 的混合:
• 像 private 成员一样,protected 成员不能被类的用户访问。
• 像 public 成员一样,protected 成员可被该类的派生类访问
需要再次强调的是:一旦函数在基类中声明为虚函数,他就一直为虚函数,派生类无法改变该函数为虚函数这一事实,派生类重定义虚函数时,可以用virtual保留字,但是不是必须这样做!

3.virtual与其他成员函数

C++ 中的函数调用默认不使用动态绑定。要触发动态绑定,满足两个条件:第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;第二,必须通过基类类型的引用或指针进行函数调用


0 0
原创粉丝点击