虚函数,虚表,虚表指针

虚函数,虚表,虚表指针 (great explanation!!!)

基类指针指向基类对象

20211015215621

#include<iostream>
using namespace std;

class A{
public:
    void f1(){
        cout<<"A f1()"<<endl;
    }
    virtual void f2(){
        cout<<"A f2()"<<endl;
    }
    virtual void f3(){
        cout<<"A f3()"<<endl;
    }
    virtual void f4(){
        cout<<"A f4()"<<endl;
    }
};
class B:public A{
public:
    void f1(){
        cout<<"B f1()"<<endl;
    }
    void f2(){
        cout<<"B f2()"<<endl;
    }
    void f4(int x){
        cout<<"B f4()"<<endl;
    }
};
int main(){
    A *p, o1;
    p=&o1;
    p->f1();    //A f1()
    p->f2();    //A f2()
    p->f3();    //A f3()
    p->f4();    //A f4()
//    p->f4(5); // 报错,因为o1对象没有形如f4(int)的函数。no matching function for call to 'A::f4(int)'
}

基类指针指向派生类对象

知识点:静态绑定,动态绑定(下的多态特性)

20211015220421

#include<iostream>
using namespace std;

class A{
public:
    void f1(){
        cout<<"A f1()"<<endl;
    }
    virtual void f2(){
        cout<<"A f2()"<<endl;
    }
    virtual void f3(){
        cout<<"A f3()"<<endl;
    }
    virtual void f4(){
        cout<<"A f4()"<<endl;
    }
};
class B:public A{
public:
    void f1(){
        cout<<"B f1()"<<endl;
    }
    void f2(){
        cout<<"B f2()"<<endl;
    }
    void f4(int x){
        cout<<"B f4()"<<endl;
    }
};
int main(){
    A *p;
    B o2;
    p=&o2;
    p->f1();    //A f1()   静态绑定,访问的是基类
    p->f2();    //B f2()   动态绑定(即:多态特性),访问的是派生类
    p->f3();    //A f3()   B类中没有f3()的实现,所以只能访问从基类继承而来的 A f3()
    p->f4();    //A f4()   同理,B类中没有形如 f4()的函数 (注意:f4()和f4(int)是不同的),所以只能访问从基类继承而来的 A f4()
//    p->f4(5); // 报错,因为虽然o2对象有形如f4(int)的函数,但是是基类指针p指向o2,p并没有访问函数f4(int)的权限。no matching function for call to 'A::f4(int)'
}

派生类指针指向派生类对象

知识点:隐藏。在隐藏中,类的函数特性只对函数的名称敏感

#include<iostream>
using namespace std;

class A{
public:
    void f1(){
        cout<<"A f1()"<<endl;
    }
    virtual void f2(){
        cout<<"A f2()"<<endl;
    }
    virtual void f3(){
        cout<<"A f3()"<<endl;
    }
    virtual void f4(){
        cout<<"A f4()"<<endl;
    }
};
class B:public A{
public:
    void f1(){
        cout<<"B f1()"<<endl;
    }
    void f2(){
        cout<<"B f2()"<<endl;
    }
//    using A::f4;   // 或者添加这句
    void f4(int x){
        cout<<"B f4()"<<endl;
    }
};
int main(){
    B *p;
    B o2;
    p=&o2;
    p->f1();    //B f1()   隐藏 基类的f1函数
    p->f2();    //B f2()   隐藏 基类的f2函数
    p->f3();    //A f3()   B类中没有f3()的实现,所以只能访问从基类继承而来的 A f3()
    p->A::f4(); //A f4()   B类中有2个f4函数(继承的和自己的)。若要调用继承的,则需要指定为A::f4(),或者在类B中添加 using A::f4;
    p->f4(5); //B f4(5) 调用自己的f4(int)函数
}

c++父类指针指向子类对象

示例1:

#include <memory>
#include <iostream>

using namespace std;
struct A {
    void foo(){
        cout<<"foo"<<endl;
    }
    virtual void bar(){
        cout<<"bar"<<endl;
    }
    A (){
        bar();
    }
};


struct B:A{
    void foo(){
        cout<<"b_foo"<<endl;
    }
    void bar(){
        cout<<"b_bar"<<endl;
    }
};

int main() {
  A* p=new B; // 调用A的构造器, 输出bar
  p->foo(); // 静态绑定,输出foo
  p->bar(); // 多态特性的动态绑定,输出b_bar
}

示例2:

#include <iostream>
using namespace std;
class A
{
public:
    virtual void foo()
    {
        cout << "A's foo()" << endl;
        bar();
    }
    virtual void bar()
    {
        cout << "A's bar()" << endl;
    }
};
class B: public A
{
public:
    void foo()
    {
        cout << "B's foo()" << endl;
        A::foo();
    }
    void bar()
    {
        cout << "B's bar()" << endl;
    }
};
int main()
{
    B bobj;
    A *aptr = &bobj;
    aptr->foo();
    cout<<"********"<<endl;
    A aobj = *aptr; //转化为A类对象
    aobj.foo();
}
//多态,调用B的foo
B's foo()
//调用A::foo();
A's foo()
//虽然是A::foo()中的bar(); 但传给指针的地址还是bobj的地址,且bar()也用virtual进行多态了。所以是B的bar()
//如果A中的bar函数不是虚函数,这里就调用的是A的bar函数
B's bar()
********
//转化为A的对象后调用A的相关函数
A's foo()
A's bar()

基类函数声明为虚函数

  • 当基类指针指向派生类对象时,为了实现完整删除,需要把基类的析构函数设为virtual
  • 当派生类指针指向派生类对象时,无需将基类的析构函数设为virtual,也可以实现完整删除
#include<iostream>
using namespace std;

class Base
{
public:
    Base(){cout<<"create Base"<<endl;}
    ~Base(){cout<<"delete Base"<<endl;}
};

class Der : public Base
{
public:
    Der(){cout<<"create Der"<<endl;}
    ~Der(){cout<<"Delete Der"<<endl;}
};

int main(int argc, char const* argv[]) {
    Der *b = new Der;
    delete b;
    return 0;
}
//create Base
//create Der
//Delete Der
//delete Base

重载,隐藏,重写

  • 重载:在同一作用域中,同名函数形式参数(参数个数、类型或者顺序)不同时,构成函数重载。(注意:只需关注同一作用域函数名称相同形参不同即可)
    • void funcA();void funcA() const;也属于重载。
      • 原因是:在类中,由于隐含的this形参的存在,const版本的function函数使得作为形参的this指针的类型变为指向const对象的指针,而非const版本的使得作为形参的this指针就是正常版本的指针。此处是发生重载的本质
      • 调用规则:const对象默认调用const成员函数,非const对象默认调用非const成员函数
    • void funcB(int a);void funcB(const int a);是非法的,不能通过编译
      • 这属于非引用传参,形参是否const是等价的
      • 当使用引用或指针传参时,有无const是不同的。指向const对象的指针(或引用)和指向非const对象的指针(或引用)做形参的函数是不同的
  • 隐藏:不同作用域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。(注意:只需关注函数名相同即可)
  • 重写:派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。(注意:函数签名必须一致基类函数是virtual的基类指针指向派生类对象,才会有多态)

相关例题

#include<iostream>
using namespace std;

void func() {
    cout << "global function"<< endl;
}

class A {
public:
    void func() {
        cout << "A function" << endl;
    }

    virtual void polyFunc(){
        cout <<"A Polymorphism_多态" <<endl;
    }
};

class B : public A {
public:
    void func() {
        cout << "B function" << endl;
    }

    void polyFunc(){
        cout <<"B Polymorphism_多态" <<endl;
    }
};

int main() {
    A *pa=new B;
    B *pb=new B;

    //    隐藏了全局函数func,调用A的func函数
    pa->func(); // 结果为:A function
    //    隐藏了全局函数func 和 A的func函数,调用B的func函数
    pb->func(); // 结果为:B function
    //    类A中的polyFunc()是虚函数,可以实现多态,因此动态绑定后调用类B的polyFunc()函数
    pa->polyFunc(); // 结果为B Polymorphism_多态
}

   转载规则


《虚函数,虚表,虚表指针》 M 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
CPP新式转型 CPP新式转型
Type Casting dynamic_cast用于父类和子类之间的转换(一般是父类向子类的安全向下转) dynamic_cast can be used only with pointers and references to ob
2020-10-16
下一篇 
在Ubuntu中使用gtest 在Ubuntu中使用gtest
GoogleTest User’s Guide官方教程Unit Testing C++ with Google Test,以及配套视频。教程简洁明了,非常推荐 安装在任意文件夹下打开终端,安装并编译gtest,并将产生的库拷贝到/usr
2020-10-12
  目录