1)只有指定为虚函数的成员函数才能进行动态绑定
2)必须通过基类的引用或者指针进行函数调用
即: 动态绑定 = 虚函数+指针/引用传递
基类代码:
class item_base { public: //virtual item_base(void); 构造函数不允许成为virtual item_base(const string &book = "", double sale_price = 0); virtual ~item_base(void); //类成员可以是虚函数,非类成员不可以 string book() const { return ISBN; }; //希望派生类继承的 virtual double net_prices(size_t n=1) const; //virtual希望派生类重定义的 virtual item_base* get_class(){ return this;}; virtual void print(ostream& os) { os << "base class:" << ISBN << endl; }; int test(); private: string ISBN; protected: //可以被派生类访问,但不能该类的普通用户访问 double price; }; void print_total(ostream& os, const item_base&item, size_t n); //virtual int fun(); //error 只有类成员可以是虚函数继承类代码:
class bulk_item: public item_base { public: bulk_item() { }; ~bulk_item() { }; string book() const { return ""; }; //希望派生类继承的,设计为非虚函数 //bulk_item(const string& str): ISBN(str), price(0) { }; /*virtual*/ double net_prices(size_t n=2) const; //虚函数不加virtual也是虚函数,永远是 //virtual item_base* get_class() { return this; }; virtual bulk_item* get_class() { return this; }; //虚函数的继承类成员可以返回派生类类型的指针或者引用 void memfcn(const bulk_item &d, const item_base &b); virtual void print(ostream& os) { item_base::print(os); os <<"derived print"<< endl; }; void print() { print(cout); }; int test(); private: size_t min_qty; double discount; };
1)虚函数的覆盖机制
在默认情况下,继承类调用虚函数,一般是使用继承类的版本,如何使用基类版本呢??如下调用就可以使用基类版本:
item_base base("hello"); bulk_item derived; derived.item_base::test(); //调用基类版本 derived.test(); //调用继承类
2) 虚函数的形参使用默认实参
如果一个调用省略了具体有默认默认值的实参,则所用的值由调用该函数的类型定义,与对象的动态类型无关。如下调用
base.net_prices(); //调用基类版本 derived.net_prices(); //调用继承类版本 derived.item_base::net_prices(); //调用基类版本
这里还不出端倪,再看下面的调用
print_total(cout, base, 3); print_total(cout, derived, 4);上面调用中我传入了实参,但是为了测试默认实参的作用,我修改了下面函数的调用方式,这里面传过来的实参就没有用了,默认使用函数声明的实参,使用如下:
//net_price使用默认实参,将由调用该函数的类型定义,与对象的动态类型无关 void print_total(ostream& os, const item_base& item, size_t n) { os << "ISBN:" << item.book() << "\tNum sold:" << n //总是调用基类的book函数,即使继承类也定义了 << "\tTotal price:" << item.net_prices() << endl; //先调用的虚函数,然后才是普通的成员函数 }另外一个需要注意的就是我在调试这个非类成员函数的时候,发现默认先是进入虚函数net_price,然后才是非虚函数book,这个是<<操作符先调用右边的表达式或者函数!这里两次调用虽然进入的函数不同,一个是基类版本,一个是继承类版本,但是传入的参数都是1,也就是基类的默认实参,看出端倪没?!通俗点讲就是谁离他最近他就使用谁,他不需要关注间接人后面的真家伙,但是动态绑定正好跟这不一样或者说是相反的,动态绑定要知道真正的家伙是谁!!。
3)函数调用的延伸
看如下的定义
base.print(cout); derived.print(cout); //会导致无穷递归自己 ,这里进行了修改 derived.print(); //会调用继承类的print然后无穷递归 item_base *bp1 = &base; item_base &br1 = base; item_base *bp2 = &derived; //动态绑定 与形参使用基类类类型传递是一致的 item_base &br2 = derived;
这里print的定义在类中已经声明,主要说一下,在继承类中有实参的虚函数继承中,如果不指定基类就会导致无穷递归,也就说使用了如下的方式:
virtual void print(ostream& os) { print(os); os <<"derived print"<< endl; };无实参的非虚函数调用的是继承类中的虚函数版本,因为当前this指针指向的是bulk_item*。
调用方式如下:
bp1->print(cout); //调用基类 br1.print(cout); //调用基类 bp2->print(cout); //调用继承类 br2.print(cout); //调用继承类这里可以看出引用于指针并无任何区别,关键的是对象本身是什么,这就需要动态运行期间才能确定,由于bp2 br2默认是指向或者绑定于继承类对象,所以在运行期间使用的是继承类版本。
原文:http://blog.csdn.net/comwise/article/details/20046869