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