相关内容:
动态类型:
动态类型是指由一个左值表达式表示的左值所引用的最终派生对象的类型。例:如果一个静态类型为“类 B ”的指针p
指向一个继承于 基类B
的派生类 D
的对象,则表达式 *p
的动态类型为“D
”。引用按照相似规则处理。一般地讲,基类的指针和基类引用有可能为动态类型,就是说在运行之前不能够确定其真实类型。通常我们说,“基类指针指向的对象的实际/真正类型”或“基类引用所引用的对象的实际/真正类型”,就是它们的动态类型。很显然,这个动态类型是 C++ 语言通过指针和引用实现运行时多态能力的核心概念。
静态类型:
是指不需要考虑表达式的执行期语义,仅分析程序文本而决定的表达式类型。静态类型仅依赖于包含表达式的程序文本的形式,而在程序运行时不会改变。通俗的讲,就是上下文无关,在编译时就可以确定其类型。
动态绑定与静态绑定:
- 静态绑定:编译时绑定,通过对象调用;
- 动态绑定:运行时绑定,通过地址实现;
- 只有采用“
指针->函数()
”或“引用变量.函数()
”的方式调用C++
类中的虚函数才会执行动态绑定。对于C++中的非虚函数,因为其不具备动态绑定的特征,所以不管采用什么样的方式调用,都不会执行动态绑定。 - 即所谓动态绑定,就是基类的指针或引用有可能指向不同的派生类对象,对于非虚函数,执行时实际调用该函数的对象类型即为该指针或引用的静态类型(基类类型);而对于虚函数,执行时实际调用该函数的对象类型为该指针或引用所指对象的实际类型。
虚函数、动态绑定、运行时多态之间的关系:
简单地说,虚函数是动态绑定的基础;动态绑定是实现运行时多态的基础。
要触发动态绑定,需满足两个条件:
(1) 只有虚函数才能进行动态绑定,非虚函数不进行动态绑定。
(2) 必须通过基类类型的引用或指针进行函数调用。
通过基类指针或基类引用做形参,当实参传入不同的派生类(或基类)的指针或引用,在函数内部触发动态绑定,从而来运行时实现多态的。
代码相关内容:
- 多态性;
- 从派生类到基类的转换;
- 引用或指针可以指向基类对象,也可以指向派生类对象;
- 只有通过引用或指针调整虚函数才会发生动态绑定;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
using namespace std;
class Item_base
{
public:
int x;
Item_base(const std::string &book = "",
double sales_price = 0.0) :isbn(book), price(sales_price) {}
std::string book() const
{
return isbn;
}
// virtual:定义为虚函数,则在派生类中可以对其进行重新定义;
virtual double net_price(size_t n) const
{
return n * price;
}
private:
string isbn;
protected:
double price;
};
class Bulk_item : public Item_base
{
public:
// 派生类的构造函数;
Bulk_item(const std::string &book = "", double sales_price = 0.0, size_t qty = 0,
double disc_rate = 0.0) :Item_base(book, sales_price), min_qty(qty), discount(disc_rate) {}
void test()
{
// cout << x << endl;
cout << price << endl; // 成员函数,可以使用父类中的protected成员;
// cout << isbn << endl; // 但是不能使用父类中的private成员;
}
void test2(const Bulk_item &d, const Item_base &b)
{
//cout << d.x << endl;
cout << d.price << endl;
//cout << b.x << endl; // public成员没问题;
//cout << b.price << endl; // 错误;protected成员可以在类的内部使用或者是对它的友元使用,可以被继承,但是不能被调用;
}
// 只有虚函数才能够重写;
double net_price(size_t cnt) const
{
if (cnt >= min_qty) //如果购买的数量大于最小打折数量,则打折;
return cnt * (1 - discount) *price;
else
return cnt * price;
}
private:
size_t min_qty;
double discount;
};
void print_total(ostream &os, Item_base *item, size_t n)
{
os << "ISBN:" << item->book() << "\tnumber sold:" << n << "\ttotal price:" << item->net_price(n) << endl;
}
int main()
{
Item_base* a = new Item_base("HTL2018-006", 11.0);
Bulk_item* b = new Bulk_item("HTL2018-007", 22.0, 2, 0.05);
print_total(cout, a, 2);
print_total(cout, b, 3); // 动态绑定:在定义的时候print_total是指向基类对象的指针或者引用,
// 在调用的时候可以根据情况调用基类对象的虚函数,也可以根据情况调用派生对象的虚函数;
// 数组中有5个指针,都指向基类对象的指针,其可以指向基类的对象,也可以指向该基类的派生类对象;(非常重要)
Item_base* books[5];
books[0] = new Item_base("HTL2018-001", 10.0);
books[1] = new Bulk_item("HTL2018-002", 20.0, 6, 0.05);
books[2] = new Item_base("HTL2018-003", 30.0);
books[3] = new Bulk_item("HTL2018-004", 40.0, 4, 0.15);
books[4] = new Bulk_item("HTL2018-005", 50.0,2,0.18);
int num[5];
num[0] = 2;
num[1] = 10;
num[2] = 1;
num[3] = 5;
num[4] = 3;
cout << endl;
for (int i = 0; i < 5; i++)
{ // 在调用的时候会根据books[i]是传入的基类对象还是派生类对象,动态调用相应的函数;实现的效果就是多态;
print_total(cout, books[i], num[i]);
}
return 0;
}代码执行结果:
参考链接:
C++中的动态类型与动态绑定、虚函数、运行时多态的实现