GeekBand C++ 面向对象高级编程(下) 第四周笔记

    xiaoxiao2025-04-19  9

       第四周的内容也是相对零零散散,有虚函数指针vptr,虚函数表vtbl的,有this指针,有动态绑定Dynamic Binfding。后边还讲到了成员函数。这次重点整理对象模型。整理内容参考百度百科,维基百科。

      虚函数:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数。

      虚函数用法格式:virtual 函数返回类型 函数名(参数表) {函数体}。

      被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,就是实现多态性(Polymorphism),多态性是将接口与实现进行分离。

      代码例:

    <span style="font-size:14px;">#include<iostream> using namespace std; class A { public: void print() { cout<<"This is A"<<endl; } }; class B : public A { public: void print() { cout<<"This is B"<<endl; } }; int main() { //为了在以后便于区分,我这段main()代码叫做main1 A a; B b; a.print(); b.print(); return 0; } </span>    输出结果分别是“This is A”、“This is B。

      通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,但并没有真正做到了多态性。多态还有个关键就是一切用指向基类的指针或引用来操作对象。现在把main()处的代码改为:

    <span style="font-size:14px;">int main() { //main2 A a; B b; A *p1 = &a; A *p2 = &b; p1->print(); p2->print(); return 0; }</span>   输出结果是两个“This is A”。

      可是p2明明指向的是class B的对象但却是调用的class A的print()函数,为了解决这个问题就需要使用虚函数:

    <span style="font-size:14px;">class A { public: virtual void print(){cout<<"This is A"<<endl;} }; class B : public A { public: void print(){cout<<"ThisisB"<<endl;} };</span>   class A的成员函数print()已经成了虚函数,class B的print()也成了虚函数。现在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。重新运行代码之后输出结果为“This is A”、“This is B。

      下面整理关于虚函数是如何做到因对象的不同而调用其相应的函数的,首先定义两个类:

    <span style="font-size:14px;">class A{//虚函数示例代码 public: virtual void fun(){cout<<1<<endl;} virtual void fun2(){cout<<2<<endl;} }; class B : public A{ public: void fun(){cout<<3<<endl;} void fun2(){cout<<4<<endl;} }; </span>   由于这两个类中有虚函数存在,所以编译器就会为他们两个分别插入一段数据,并为他们分别创建一个表。那段数据叫做vptr指针,指向那个表。那个表叫做vtbl。每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,可以把vtbl形象地看成一个数组,这个数组的每个元素存放的就是虚函数的地址。   this指针:一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。

      this指针使用:1.在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this;2.当参数与成员变量名相同时使用this指针。this指针是类的一个自动生成、自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象。全局仅有一个this指针,当一个对象被创建时,this指针就存放指向对象数据的首地址。

     this指针例:

    <span style="font-size:14px;">#include<iostream> using namespace std; class Point { private: int x,y; public: Point(int a,int b) { x=a; y=b; } void MovePoint(int a,int b) { x+=a; y+=b; } void print() { cout<<"x="<<x<<"y="<<y<<endl; } }; int main() { Point point1(10,10); point1.MovePoint(2,2); point1.print(); return 0; }</span>   当对象point1调用MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。

      Dynamic Binfding:动态绑定是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。程序运行过程中,把函数调用与响应调用所需要的代码相结合的过程称为动态绑定。动态绑定是将一个过程调用与相应代码链接起来的行为。是指与给定的过程调用相关联的代码,只有在运行期才可知的一种绑定,他是多态实现的具体形式。

      Dynamic Binfding原理:1.C++中,通过基类的引用或指针调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的。                                               2.C++中动态绑定是通过虚函数实现的。而虚函数是通过一张虚函数表(virtual table)实现的。这个表中记录了虚函数的地址,解决继承、覆盖的问题,保证动态绑定时能够根据对象的实际类型调用正确的函数。                                               3.在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

    转载请注明原文地址: https://ju.6miu.com/read-1298240.html
    最新回复(0)