> 文章列表 > 【C++】多态虚表

【C++】多态虚表

【C++】多态虚表

多态——多种形态

多态的分类:

1.静态多态:函数重载和运算符重载(复用函数名)

2.动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态的区别:

静态多态的函数地址早绑定——编译阶段确定函数地址

动态多态的函数地址晚绑定——运行阶段确定函数地址

另一种分类:

1.重载多态--函数重载&运算符重载

2.包含多态--virtual

3.强制多态--强制类型转换 _cast

4.参数多态--模板


地址早绑定和晚绑定

下面代码中父类:Animal,成员函数:speak,子类:Cat,成员函数:speak;

函数地址早绑定:

先使用父类的构造函数,输出animal speak。

class Animal
{
public:void speak(){ cout << "animal speak" << endl; }
};
class Cat :public Animal
{
public:void speak() { cout << "cat speak" << endl; }
};
void doSpeak(Animal &animal)
{animal.speak();
}
int main1()
{Cat cat;doSpeak(cat);//animal speak//父子之间允许类型转换,函数地址早绑定return 0;
}

函数地址晚绑定:

父类中使用虚函数,子类重写speak函数覆盖,输出cat speak。

实现动态多态:

1.有继承关系

2.子类要重写(函数返回值类型、名称、参数列表都相同)父类的虚函数

动态多态的使用:父类的 指针 或者 引用 执行子类对象!!!

子类将父类的虚函数表继承下来,如果子类重写了这个虚函数,则会将之前的虚函数覆盖,但父类自己的没有改变,当父类的指针或引用指向子类对象时,发生多态。

如果是基类同名同参的虚函数,则子类中可以不用写virtual。

class Animal
{
public:virtual void speak() { cout << "animal speak" << endl; }//加一个虚的关键字
};
class Cat :public Animal
{
public:void speak() { cout << "cat speak" << endl; }
};
void doSpeak(Animal &animal)
{animal.speak();
}
int main()
{Cat cat;doSpeak(cat);//cat speakreturn 0;
}

虚表

类中如果有虚函数,则会有一个(继承来的会有多个)vfptr虚函数指针。

如果是个空类,则大小为1个字节:占位。

在不算继承的情况下,如果里面只有虚函数(无论多少个),则为四个字节,一个指针的大小。指针指向一个虚表,虚表里面放着虚函数的入口地址。

class A
{
public:virtual void fa() { cout << "A::fa" << endl; }virtual void ga() { cout << "A::ga" << endl; }virtual void ha() { cout << "A::ha" << endl; }
private:int m_i;int m_j;
};
int main()
{A a;    
}
【C++】多态虚表

且其他成员地址都在虚表之后

【C++】多态虚表
【C++】多态虚表

可以把这个虚表看作是函数指针数组,可以定义一个函数指针指向这个数组,

int main()
{A a;typedef void (*Fun)();//函数指针Fun pf = NULL;pf = (Fun) * ((int*)*(int*)(&a));pf(); //A::fapf = (Fun) * ((int*)*(int*)(&a) + 1);pf();//A::fbpf = (Fun) * ((int*)*(int*)(&a) + 2);pf();//A::fc
}

类的地址第一个内容就是这个虚表地址:(int*)(&a),//如果不强转的话&a是a*类型的

fa的地址为虚表第一个元素(解引用):(int*)*(int*)(&a),

最后再解引用就是fa函数:*((int*)*(int*)(&a))

之后转成函数指针类型:(Fun)*((int*)*(int*)(&a))

就可以用函数指针调用虚函数了

虚表的个数

对于多继承,每个父类有自己的虚表。子类将新的虚函数放到第一个父类的虚表中。

下面A有一个虚指针,B有一个虚指针,C有一个虚指针,D继承了ABC的虚指针,并将自己独有的虚函数地址放在继承下来的A的虚表中

class A
{
public:virtual void fa() { cout << "A::fa" << endl; }virtual void ga() { cout << "A::ga" << endl; }
};
class B
{
public:virtual void fb() { cout << "B::fb" << endl; }virtual void gb() { cout << "B::gb" << endl; }
};
class C
{
public:virtual void fc() { cout << "C::fc" << endl; }virtual void gc() { cout << "C::gc" << endl; }
};
class D :public A, public B, public C
{
public:virtual void fd() { cout << "D::fd" << endl; }virtual void gd() { cout << "D::gd" << endl; }
};
void main()
{D d;cout << sizeof(D) << endl;//12,继承了三个虚指针,修改了第一个typedef void (*Fun)();Fun pf = (Fun) * ((int*)(*(int*)(&d)) + 1);pf();//A::gapf = (Fun) * ((int*)(*(int*)(&d)) + 2);pf();//D::fdpf = (Fun) * ((int*)(*(int*)(&d)) + 3);pf();//D::gd
}
【C++】多态虚表