谁知道 C++ 中 虚函数的工作机制????
发布网友
发布时间:2022-03-18 00:38
我来回答
共5个回答
懂视网
时间:2022-03-18 04:59
虚函数实现了c++的多态的机制,多态就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。下面来看一段简单的代码。当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同、参数列表完全一致、返回值类型相关)自动成为虚函数。
热心网友
时间:2022-03-18 02:07
C++类对象在内存中分配很复杂,静态变量和一半的函数存储在类的信息中,而非静态变量和虚函数存储在对象中。类对象不只是存储成员变量,对象中存储一个虚函数表的指针,即:vtbl,虚函数表中就存储着虚函数的指针,多少个虚函数就有多少项。但vtbl是一个指向虚函数表的指针,所以对象内存不变,变化的只是虚函数表。而继承、多态的机制就是靠这个虚函数表实现的。
派生类将虚函数重载时,会将虚函数表中此虚函数对应的函数指针修改。
所以将此派生类对象地址赋值给基类指针时,虚函数表中的函数指针并没有改变,所以用基类指针调用此虚函数其实调用的是派生类实现的函数。这就实现了多态。
而将派生类对象赋值给基类对象是,成员变量存在cut,会丢失派生类多于的数据,而虚函数表是不会复制的,虚函数表是按照基类创建的,所以将派生类对象复制给基类对象不能实现多态调用。
具体中间原理特别复杂,我也只了解大概。
推荐看一本书《Inside C++ Object Modol》,将的是C++对象的内部实现机制。很好的一本书。中文版侯捷翻译。叫《深入探索C++对象模型》
热心网友
时间:2022-03-18 03:25
从语言和概念的角度来说,子类继承父类是继承了所有protected和public非static成员(static成员是共享而不是继承),而成员包括函数和数据。所以从概念上来说,成员函数和成员变量都被继承了。
从实现上来说,实现要在外显上不违反概念,在内部可以非常随意。所有的实现子类继承父类成员函数只是子类共享了父类的函数,没有两份拷贝。编译器知道父类的某成员函数的地址,那么在子类调用父类的该方法时,编译器只要产生
call 函数地址 的代码就可以调用父类的方法,完全没有必要保留两份拷贝。
换句话来说,非虚方法的调用在编译时就已经确定了,编译器掌握一切信息。编译器选择最高效的实现(只保留一份函数拷贝)来符合C++语言概念。从逻辑上认为子类继承了父类的方法。
对虚函数,如果是在子类对象上用.操作符调用虚函数,那么编译器可以确定函数地址,此时不需要使用虚函数调用机制。只有在基类指针上用->操作符调用虚函数时,才会使用虚函数调用机制。此时编译器不知道基类指针指向具体什么类型的对象,所以它不知道到底调用哪个函数,它产生类似如下代码:
call [vftable + functionoffset]
也就是通过虚函数表中对应函数偏移来调用,虚函数表的布局是编译器已知的。当具体某个对象构造时,他们初始化虚表使得其中指针都指向自己的虚函数。这样就通过这个实现来满足了C++的虚函数概念。
举个例子:
class base {
private:
int member1;
protected:
int member2;
public:
int fun();
virtual int virtualfun();
};
class derive : public base {
public:
int member;
public:
virtual int virtualfun();
};
在代码中使用:derive test;
(以微软c/c++编译器为例)那么test在内存中可能是如下布局:
110000 vftable----------->112000
110004 base::member1
110008 base::member2
11000c derive::member
----------------------------
112000 (vftable[0])derive::virtualfun------>410000
----------------------------
410000 int derive::virtualfun()
410500 int base::virtualfun()
411000 int base::fun()
另外,团IDC网上有许多产品团购,便宜有口碑
热心网友
时间:2022-03-18 05:00
从语言和概念的角度来说,子类继承父类是继承了所有protected和public非static成员(static成员是共享而不是继承),而成员包括函数和数据。所以从概念上来说,成员函数和成员变量都被继承了。
从实现上来说,实现要在外显上不违反概念,在内部可以非常随意。所有的实现子类继承父类成员函数只是子类共享了父类的函数,没有两份拷贝。编译器知道父类的某成员函数的地址,那么在子类调用父类的该方法时,编译器只要产生
call 函数地址 的代码就可以调用父类的方法,完全没有必要保留两份拷贝。
换句话来说,非虚方法的调用在编译时就已经确定了,编译器掌握一切信息。编译器选择最高效的实现(只保留一份函数拷贝)来符合C++语言概念。从逻辑上认为子类继承了父类的方法。
对虚函数,如果是在子类对象上用.操作符调用虚函数,那么编译器可以确定函数地址,此时不需要使用虚函数调用机制。只有在基类指针上用->操作符调用虚函数时,才会使用虚函数调用机制。此时编译器不知道基类指针指向具体什么类型的对象,所以它不知道到底调用哪个函数,它产生类似如下代码:
call [vftable + functionoffset]
也就是通过虚函数表中对应函数偏移来调用,虚函数表的布局是编译器已知的。当具体某个对象构造时,他们初始化虚表使得其中指针都指向自己的虚函数。这样就通过这个实现来满足了C++的虚函数概念。
举个例子:
class base {
private:
int member1;
protected:
int member2;
public:
int fun();
virtual int virtualfun();
};
class derive : public base {
public:
int member;
public:
virtual int virtualfun();
};
在代码中使用:derive test;
(以微软c/c++编译器为例)那么test在内存中可能是如下布局:
110000 vftable----------->112000
110004 base::member1
110008 base::member2
11000c derive::member
----------------------------
112000 (vftable[0])derive::virtualfun------>410000
----------------------------
410000 int derive::virtualfun()
410500 int base::virtualfun()
411000 int base::fun()
另外,站长团上有产品团购,便宜有保证
热心网友
时间:2022-03-18 06:51
类中的函数,只有名,没有内容,访问对象时
仍被调用,目的是让你的实际函数取代虚函数,
扩大功能。