首页天道酬勤虚函数的原理(c语言虚函数是什么)

虚函数的原理(c语言虚函数是什么)

admin 11-29 01:51 235次浏览

首先要知道函数是有地址的,虚函数也不例外。虚拟函数的地址存储在虚拟函数表中,运行时

多态是通过虚函数和虚函数表实现的。类对象中会有一个指向虚拟表的指针。通过这个

指针调用虚函数,虚函数的调用将被编译器转换为对虚拟表的访问。虚拟表指针的名称也会被编译器修改,所以在多重继承下,类内可能有多个虚拟表指针。

==========华丽的分界线===========

虚函数提供动态多态,但也可以抑制。让我们看看下面的例子。

甲级

{

公众号:

virtual void f(){ STD : cout ' a 1:3360 f ' STD : endl;}

};

乙级:公共甲级

{

公众号:

virtual void f(){ STD : cout ' b :3360 f ' STD : endl;}

};

int main()

{

乙乙;

b . f();//因为B重载了F的实现,所以会调用B的F。

//那我们怎么才能调用基类的F呢?我们可以通过域名运营商来实现。

b.a : f();

}

==========华丽的分界线===========

hxdjm的派生类中只有一个虚函数表。这个虚函数表和基类的虚函数表不是一个表。举个例子吧。

A1类

{

公众号:

A1(int _a1=1):a1(_a1){}

virtual void f(){ STD : cout ' a 1:3360 f ' STD : endl;}

virtual void g(){ STD : cout ' a 1 :3360g ' STD : endl;}

私人:

int a1

};

b 1:公共A1类

{

公众号:

B1(int _a1=1,int _b1=4):A1(_a1),b(_b1){}

私人:

int b;

};

int main()

{

B1 b1

B1 . g();

返回0;

}

如果派生类没有重写基类的虚函数,则派生类中虚函数表的虚函数地址与基类的虚函数地址相同。

==========华丽的分界线===========

在多重继承的情况下,派生类中有多个虚函数表,虚函数表的排列与继承顺序一致,派生类会重写函数。

覆盖所有同名虚函数表的内容,派生类定义的新虚函数将扩展到第一类虚函数表之后。

这里有一个例子:

A1类

{

公众号:

A1(int _a1=1) : a1(_a1) { }

虚空f(){ cout ' a 1: f ' endl;}

虚空g(){ cout ' a 1:g ' endl;}

虚空h(){ cout ' a 1:h ' endl;}

~A1() {}

私人:

int a1

};

A2类

{

公众号:

A2(int _a2=2) : a2(_a2) { }

虚空f(){ cout ' a 2: f ' endl;}

虚空g(){ cout ' a 2:g ' endl;}

虚空h(){ cout ' a 2:h ' endl;}

~A2() {}

私人:

int a2

};

A3级

{

公众号:

A3(int _a3=3) : a3(_a3) { }

虚空f(){ cout ' a : f ' endl;}

虚拟空间g() { cou

t << "A3::g" << endl; }

virtual void h() { cout << "A3::h" << endl; }

~A3() {}

private:

int a3;

};

class B : public A1, public A2, public A3

{

public:

B(int _a1 = 1, int _a2 = 2, int _a3 = 3, int _b = 4) :A1(_a1), A2(_a2), A3(_a3), b(_b) { }

virtual void f1() { cout << "B::f" << endl; }

virtual void g1() { cout << "B::g" << endl; }

virtual void h1() { cout << "B::h" << endl; }

private:

int b;

};

==========华丽的分割线==============

如果多重继承的情况下,派生类重载了基类的虚函数,虚表是什么样子的呢?

下面我们上代码看看

class A1

{

public:

A1(int _a1 = 1) : a1(_a1) { }

virtual void f() { cout << "A1::f" << endl; }

virtual void g() { cout << "A1::g" << endl; }

virtual void h() { cout << "A1::h" << endl; }

~A1() {}

private:

int a1;

};

class A2

{

public:

A2(int _a2 = 2) : a2(_a2) { }

virtual void f() { cout << "A2::f" << endl; }

virtual void g() { cout << "A2::g" << endl; }

virtual void h() { cout << "A2::h" << endl; }

~A2() {}

private:

int a2;

};

class A3

{

public:

A3(int _a3 = 3) : a3(_a3) { }

virtual void f() { cout << "A3::f" << endl; }

virtual void g() { cout << "A3::g" << endl; }

virtual void h() { cout << "A3::h" << endl; }

~A3() {}

private:

int a3;

};

class B : public A1, public A2, public A3

{

public:

B(int _a1 = 1, int _a2 = 2, int _a3 = 3, int _b = 4) :A1(_a1), A2(_a2), A3(_a3), b(_b) { }

virtual void f() { cout << "B::f" << endl; }

virtual void g() { cout << "B::g" << endl; }

virtual void h() { cout << "B::h" << endl; }

private:

int b;

};

从编译器给出的信息我们可以看到在第二个虚函数表中有adjustor{8}的字样,这就是A类的大小,也就是

说这就是告诉编译器需要进行8字字节的偏移。当B类用不同的基类指针指向的时候,运行的是不同的基类中的

虚函数,例如A2类指向B的时候虚函数指针是自动跳到B类中的A2类所在的地方的。经过这样的调整

A1 A2 A3都会指向正确的类的位置。当B类重写了函数之后 A2 A3的虚函数表所指向的已经不再是简单的函数指针了

而是一个trunk对象,这就是c++的trunk技术。所谓的trunk就是一段汇编代码 这段汇编代码可以适当的偏移来

调整this指针来跳到对应的虚函数中去 并调用这个函数,也就是当使用A1的指针指向B的对象的时候 不需要发生偏移

而使用A2指向B时则需要进行sizeof(A1)字节的偏移,并跳转到A2中的函数来执行,这就是通过trunk的jump

指令跳转到这个函数的。

Hue开发指南 托管Hadoop集群 UHadoopjquery能不能删除id相同的元素
纯虚函数如何定义(哪些函数可以定义为虚函数) 经验设计法的特点(plc移植法的特点)
相关内容