C++中虚函数的动态绑定和多态性

目录

    • 静态类型VS动态类型,静态绑定VS动态绑定两组概念
    • 虚函数的实现机制
    • 多态性

一.静态 vs 动态

  静态类型 VS 动态类型。静态类型指的是对象声明的类型,在编译器确定的。动态类型指的是对象的所指向的类型,动态类型是可以更改的,静态类型无法更改。继承关系会导致对象的指针和引用具有静态类型和动态类型,因为继承关系的存在中可能会存在类型之间的向上向下类型转换。静态绑定 VS 动态绑定。某特性(函数)依赖与对象的静态类型,在编译期确定,某特性(函数)依赖于对象的动态类型,在运行期确定。只有通过基类的引用或者指针调用虚函数时,才能发生动态绑定,如果使用对象来操作虚函数的话,仍然会采用静态绑定的方式。因为引用或者指针既可以指向基类对象,也可以指向派生类对象的基类部分,用引用或者指针调用的虚函数。例如下面的例子中,pd的静态类型是D*,pb的静态类型是B*,是在编译器确定的;pd和pb的动态类型都是D*。DoSomething()普通的成员函数是在编译器静态绑定的,调用各自的静态类型中的方法。vfun()是虚成员函数,虚函数是动态绑定的,因此调用动态类型的方法,也就是D类型中vfun。

 1 #include <iostream>
 2 using namespace std;
 3
 4 class B
 5 {
 6 public:
 7     void DoSomething(){
 8         std::cout << "B::DoSomething()" << endl;
 9     }
10     virtual void vfun(){
11         std::cout << "B::vfun()" << endl;
12     }
13 };
14
15 class D : public B
16 {
17 public:
18     void DoSomething(){
19         std::cout << "D::DoSomething()" << endl;
20     }
21     virtual void vfun(){
22         std::cout << "D::vfun()" << endl;
23
24     }
25 };
26
27 int main()
28 {
29     D* pd = new D();       //指针存在注意静态类型和动态类型
30     B* pb = pd;            //指正注意静态类型和动态类型
31     pd->DoSomething();     //普通成员函数,静态绑定静态类型中的方法
32     pb->DoSomething();     //普通成员函数,静态绑定静态类型中的方法
33
34     pd->vfun();            //虚成员函数,动态绑定到动态类型中的方法
35     pb->vfun();        //虚成员函数,动态绑定到动态类型中的方法
36     return 0;
37 }

二.虚函数的的实现机制

二、C++多态性

1.多态性的定义:同一操作作用于不同的对象,可以有不同的解释,即产生不同的执行效果。多态性有两种,编译时的多态,也就是函数重载;运行时多态,在系统运行时确定,是通过虚成员实现的。一般指的是后者

2.典型用例

 1 #include <iostream>
 2 using namespace std;
 3
 4 class Person
 5 {
 6 public:
 7     virtual void print(){
 8         std::cout << "I‘m a person" << endl;
 9     }
10
11 };
12
13 class Chinese : public Person
14 {
15 public:
16     virtual void print(){
17         std::cout << "I‘m as Chinese" << endl;
18     }
19
20 };
21
22 class American : public Person
23 {
24 public:
25     virtual void print(){
26         std::cout << "I‘m as American" << endl;
27     }
28
29 };
30
31
32 //reference
33 void printPerson(Person& person){
34     person.print();
35 }
36 //pointer
37 void printPerson(Person* p){
38     p->print();
39 }
40 int main()
41 {
42     Person p;
43     Chinese c;
44     American a;
45     printPerson(p);
46     printPerson(c);
47     printPerson(a);
48
49     printPerson(&p);
50     printPerson(&c);
51     printPerson(&a);
52
53     return 0;
54 }

4.多态的实现:多态是由继承和虚函数实现的,因为虚函数是动态绑定的,依赖于调用对象(静态类型为基类的指针和引用)的动态类型,所以根据动态类型的不同,而导致操作不同,也就是多态性。

3.虚函数的实现:简单的说虚函数是通过虚函数表来实现的。

每个带有虚函数的类,都会有一个虚函数表(vtbl),表中的每一项记录它一个的虚函数的地址。实际上一个函数指针的数组。类的对象的最前面存储虚函数表的地址。

虚函数表在类的继承中也会继承和重写,当有重写发生时,就会产生多态性。

 1 #include <iostream>
 2 using namespace std;
 3 class Person
 4 {
 5 public:
 6     virtual void print(){
 7         std::cout << "I‘m a person" << endl;
 8     }
 9     virtual void foo(){}
11 };
12
13 class Chinese : public Person
14 {
15 public:
16     virtual void print(){
17         std::cout << "I‘m as Chinese" << endl;
18     }
19     virtual void foo2(){}
20 };
21 class American : public Person
22 {
23 public:
24     virtual void print(){
25         std::cout << "I‘m as American" << endl;
26     }
27 };
28 //reference
29 void printPerson(Person& person){
30     person.print();
31 }
32 //pointer
33 void printPerson(Person* p){
34     p->print();
35 }
36 int main()
37 {
38     Person p;
39     Chinese c;
40     American a;
41     printPerson(&p);
42     printPerson(&c);
43     printPerson(&a);
44     return 0;
45 }

Person 类的vtbl : Person::print()的地址,Person::foo()的地址

Chinese类的地址:Chinese::print()的地址,Person::foo()的地址,Person::foo1()的地址

American类的地址:American::print()的地址,Person::foo()的地址

三、总结

  多态是C++的三大特性之一,非常重要,产生的条件是继承关系,基类中存在虚成员函数,派生类override(重写覆盖)基类的的虚成员函数,在代码上表现为基类对象的指针或引用调用虚成员函数,运行结果表现为实际调用各自派生类重写的函数。

多态的核心虚成员函数是动态绑定的,即依赖于对象的动态类型。理解多态的难点是虚函数的实现机制,即虚函数表。

参考:http://blog.csdn.net/chgaowei/article/details/6427731

时间: 2024-10-12 16:35:44

C++中虚函数的动态绑定和多态性的相关文章

[转载]虚函数在构造函数中,已经失去了虚函数的动态绑定特性

class A { public: A() { Print(); } virtual void Print() { printf("A is constructed.\n"); } }; class B: public A { public: B() { Print(); } virtual void Print() { printf("B is constructed.\n"); } }; int _tmain(int argc, _TCHAR* argv[])

c++之虚函数(动态绑定技术)

 虚函数:    1.虚函数的思想借鉴于Simula语言,可以实现运行的多态.虚函数允许派生类重载.覆盖,即使访问对象所使用的指针类型是基类型,但依旧能调用派生类的相应函数.当使用对象指针或对象引用调用虚函数时,采用动态绑定技术. 2.virtual 关键字只能用在虚函数的声明中,在函数的定义中不能使用 class Shape{     public:    virtual void draw();};void Shape::draw(){cout<<"这是Shape->dra

&lt;转&gt;C++继承中虚函数的使用

转自:http://blog.csdn.net/itolfn/article/details/7412364 一:继承中的指针问题. 1. 指向基类的指针可以指向派生类对象,当基类指针指向派生类对象时,这种指针只能访问派生对象从基类继承 而来的那些成员,不能访问子类特有的元素 ,除非应用强类型转换,例如有基类B和从B派生的子类D,则B *p;D  dd; p=&dd;是可以的,指针p只能访问从基类派生而来的成员,不能访问派生类D特有的成员.因为基类不 知道派生类中的这些成员. 2. 不能使派生类

继承中虚函数的缺省参数值问题

如果类继承中重新定义了虚函数,那么虚函数中的缺省参数不要重新定义. 用一句话来解释原因就是:虚函数是动态绑定的(dynamically bound),但是缺省参数却是静态绑定的(statically bound). 静态类型和动态类型 首先需要了解什么是对象的静态类型和动态类型,对象的所谓静态类型(static type),就是它在程序中被声明时所采用的类型. 以下面的类为例: class Shape { public: enum ShapeColor{Red,Green,Blue}; virt

关于C++与Java中虚函数问题的读书笔记

之前一直用C++编程,对虚函数还是一些较为肤浅的理解.可近期由于某些原因搞了下Java,发现有些知识点不熟,于是站在先驱巨人的肩上谈谈C++与Java中虚函数问题. Java中的虚函数 以下是段别人的代码,输入结果竟是Base,这让一直以来用C/C++的我有些莫不着头脑,不是Java里对象是引用吗?C/C++中通过指向基类的指针或引用来指向派生类,那么对于虚函数来说通过基类指针使用的是指向的派生类.但在Java中没有keyword标明虚函数,此时就不是非常明确究竟调用的谁. class base

c++中虚函数和多态性

1.直接看下列代码: #include <iostream> using namespace std; class base{ public: void who(){ cout<<"this is the class of base!"<<endl; } }; class derivel1:public base{ public: void who(){ //一般的重载函数 cout<<"this is the class of

C++中虚函数的作用是什么?它应该怎么用呢?

虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始) 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略.下面来看一段简单的代码 class A{

c++中虚函数和纯虚函数定义

只有用virtual声明类的成员函数,使之成为虚函数,不能将类外的普通函数声明为虚函数.因为虚函数的作用是允许在派生类中对基类的虚函数重新定义.所以虚函数只能用于类的继承层次结构中. 一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数. 根据什么考虑是否把一个成员函数声明为虚函数? ①  看成员函数所在的类是否会作为基类 ② 看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该

C++中虚函数和纯虚函数的作用与区别-详解

虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入原因:为了方便使用多态特性,我们常常需要在基类中定义虚函数. class Cman { public: virtual void Eat(){--}; void Move(); private: }; class CChild : public CMan { public: virtual void