C++ 动态绑定
C++ 的虚函数 + 派生类对象及派生类到基类的类型转换 共同完成了C++ 面向对象程序设计的三个基本概念之一--动态绑定。
同样一段代码运行时才能决定运行的具体函数是哪个(相对于编译时就能确定而言),这个概念在C++ 中称为动态绑定或运行时绑定(run-time binding)。
例如,定义下面一个接口用于打印书籍的总价格,这个接口既可以用于打印没有折扣的书籍价格(基类 Quote 定义的对象),也可以打印有折扣的书籍价格(派生类 Bulk_quote 定义的对象)。
至于,item.net_price() 到底调用基类中的net_price()还是派生类中的net_price()取决于 print_total() 这个函数运行时的实参, const Quote &item 这个引用是基类定义的对象还是派生类定义的对象。
double print_total(const Quote &item, size_t n) {
double ret = item.net_price(n);
cout << "Total: " << ret << endl;
return ret;
}
基类如果希望派生类覆盖自己定义的某个函数,需要通过加上virtual 关键字,声明为虚函数。
如果基类把一个函数声明为虚函数,则该函数在派生类中隐式的也是虚函数。
动态绑定还用到一个概念就是派生类对象以及派生类到基类的类型转换。
正是因为派生类对象中含有与其基类对应的部分,C++ 能将派生类的对象当成基类对象来使用,也可以将基类的指针或引用绑定到派生类对象的基类部分上。
如:
Quote item; // 基类对象
Bulk_quote bulk; // 派生类对象
Quote *p = &item; // p指向Quote 对象
p = &bulk; // p 指向bulk 的Quote部分
Quote &r = bulk; // r 绑定到bulk 的Quote 部分
如果基类中某个函数没有被声明为虚函数,派生类中也定义了同原型的函数的话,通过基类指针访问派生类定义的对象,调用的函数是基类中的定义,而不是派生类中的定义。
这也是增加一个 virtual 关键字的原因所在。
#include <iostream>
using namespace std;
#define VIRTUAL_FUNC
// Base class defination
class Quote {
public:
Quote() = default;
Quote(const std::string &book, double sales_price) :
bookNo(book), price(sales_price) {}
std::string isbn() const {
return bookNo;
}
#if defined (VIRTUAL_FUNC)
virtual double net_price(std::size_t n) const {
cout << "Quote" << endl;
return n * price;
}
#else
virtual double net_price(std::size_t n) const = 0;
#endif
virtual ~Quote() = default;
private:
std::string bookNo;
protected:
double price;
};
// Derived class defination
class Bulk_quote: public Quote {
public:
Bulk_quote()= default;
//Bulk_quote(const std::string &book, double sales_price, std::size_t qty, double dis);
Bulk_quote(const std::string &book, double sales_price, std::size_t qty, double dis):min_qty(qty), discount(dis), Quote(book, sales_price) { }
// double net_price(std::size_t n) const {
//cout << "hello" << endl;
// }
double net_price(std::size_t n) const {
cout << "Bulk_quote" << endl;
return n * price * discount;
}
private:
std::size_t min_qty;
double discount;
};
double print_total(const Quote &item, size_t n) {
double ret = item.net_price(n);
cout << "Total: " << ret << endl;
return ret;
}
//Quote item;
Quote basic_item("0-123", 10);
Bulk_quote item("0-123", 10, 3, 0.80);
Quote *p_item = &basic_item;
Quote *q_item = &item;
//Bulk_quote *q_item = &item;
int main() {
cout << print_total(*p_item, 5) << endl;
cout << print_total(*q_item, 5) << endl;
return 0;
}
运行结果:
Quote
Total: 50
50
Bulk_quote
Total: 40
40