C++之旅 虚函数

1、为什么了解决什么

多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。(静态绑定和动态绑定)

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

C++中,实现多态有以下方法:虚函数,抽象类,覆盖,模板(重载和多态无关)。

多态就是允许方法重名 参数或返回值可以是父类型传入或返回。允许将子类类型的指针赋值给父类类型的指针。

其实如果是这样的目的话,函数指针也可以实现多态。或我们反过来虚函数就是通过类名查找其虚表指针*__vptr(void**)。

2、定义

简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。它的一般实现模型是:每一个类(class)有一个虚表(virtual table),内含该class之中有作用的虚(virtual)函数的地址,然后每个对象有一个vptr,指向虚表(virtual table)的所在。

这个类大小就会多4个字节,这4个字节就是VTABLE的地址,这个类里的每个虚函数都是这个VTABLE数组的成员。

3、实现原理工作机制

我们只要有虚函数的类,编译器都会为这个类创建一个VTABLE,用它来保存该类的所有虚函数地址。我们叫其函数指针数组。这个数组每个元素就是虚函数的地址。编译器给VTABLE置入了一指针VPTR。VPTR就是VTABLE的入口地址。VPTR的值取决于对像内存的地址。

每个类其VTABLE是独立唯一的,也就是说当构造该派生类实例(对象)时,那么这个类会有一个virtual table,而每一个实例(对象)都会有一个virtual pointer(以下简称vptr)指向该类的virtual function tabl

4、例题:#include <iostream>

//www.realtoptv.com by zhangyj

//========================================================

//                 名字空间声明

//========================================================

using std::cout;

using std::endl;

//Y = 0.30R + 0.59G + 0.11B , U = 0.493(B-Y), V = 0.877(R-Y)

//R = Y + 1.4075 *(V - 128)

//G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)

//B = Y + 1.779 *(U – 128)

//========================================================

//========================================================

class RGB2YUV

{

public:

//默认构造函数

RGB2YUV(int r = 1, int g = 1, int b = 1) :

m_r(r), m_g(g), m_b(b)

{

cout << "RGB2YUV constructor called" << endl;

}

//定义为虚函数

virtual int Y()

{

return  0.30*m_r + 0.59*m_g + 0.11*m_b;

}

virtual int U()

{

return  0.493*( m_b - Y() );

}

virtual  int V()

{

return 0.877*(m_r - Y());

}

//显示

void ShowY()

{

cout << "Y volume is " << Y() << endl;

}

void ShowU()

{

cout << "U volume is " << U() << endl;

}

void ShowV()

{

cout << "V volume is " << V() << endl;

}

protected:

int m_r;

int m_g;

int m_b;

};

//========================================================

//========================================================

class PS_RGB2YUV : public RGB2YUV

{

public:

PS_RGB2YUV(int r, int g, int b) :RGB2YUV(r, g, b){}

//定义为虚函数

virtual int Y()

{

return  0.30*m_r + 0.59*m_g + 0.11*m_b+1;

}

virtual int U()

{

return  0.493*(m_b - Y())+1;

}

virtual int V()

{

return 0.877*(m_r - Y())+1;

}

};

//========================================================

//函数名   :main

//函数说明 虚函数的实现机制

//函数参数 :无

//返回值   :0

//========================================================

int main()

{

RGB2YUV Wyuv(32, 32, 32);                    //基类对象定义

PS_RGB2YUV Wyuv_offset(32, 32, 32);    //派生类对象定义

//显示

Wyuv.ShowY();

Wyuv.ShowU();

Wyuv.ShowV();

//显示派生类对象

Wyuv_offset.ShowY();

Wyuv_offset.ShowU();

Wyuv_offset.ShowV();

//显示类的大小

cout << "Wyuv‘s size is " << sizeof(Wyuv) << endl;

cout << "Wyuv_offset‘s size is " << sizeof(Wyuv_offset) << endl;

system("pause");

}

8、反汇编分析

//定义为虚函数

29:     virtual int Y()

30:     {

013C4AA0 55                   push        ebp

013C4AA1 8B EC                mov         ebp,esp

013C4AA3 81 EC CC 00 00 00    sub         esp,0CCh

013C4AA9 53                   push        ebx

013C4AAA 56                   push        esi

013C4AAB 57                   push        edi

013C4AAC 51                   push        ecx

013C4AAD 8D BD 34 FF FF FF    lea         ediebp-0CCh]

013C4AB3 B9 33 00 00 00       mov         ecx,33h

013C4AB8 B8 CC CC CC CC       mov         eax,0CCCCCCCCh

013C4ABD F3 AB                rep stos    dword ptr es:[edi]

013C4ABF 59                   pop         ecx

013C4AC0 89 4D F8             mov         dword ptr [this],ecx

//每个实例都有this,寄存器ECX里面的值作为this指针地址

31:         return  0.30*m_r + 0.59*m_g + 0.11*m_b;

013C4AC3 8B 45 F8             mov         eax,dword ptr [this]

013C4AC6 F2 0F 2A 40 04       cvtsi2sd    xmm0,dword ptr [eax+4]

013C4ACB F2 0F 59 05 60 DC 3C 01 mulsd       xmm0,mmword ptr ds:[13CDC60h]

013C4AD3 8B 4D F8             mov         ecx,dword ptr [this]

013C4AD6 F2 0F 2A 49 08       cvtsi2sd    xmm1,dword ptr [ecx+8]

013C4ADB F2 0F 59 0D C8 DC 3C 01 mulsd       xmm1,mmword ptr ds:[13CDCC8h]

013C4AE3 F2 0F 58 C1          addsd       xmm0,xmm1

013C4AE7 8B 55 F8             mov         edx,dword ptr [this]

013C4AEA F2 0F 2A 4A 0C       cvtsi2sd    xmm1,dword ptr [edx+0Ch]

013C4AEF F2 0F 59 0D B0 DB 3C 01 mulsd       xmm1,mmword ptr ds:[13CDBB0h]

013C4AF7 F2 0F 58 C1          addsd       xmm0,xmm1

013C4AFB F2 0F 2C C0          cvttsd2si   eax,xmm0

32:     }

virtual int Y()//调了2次值

74:     {

013C4A30 55                   push        ebp

013C4A31 8B EC                mov         ebp,esp

013C4A33 81 EC CC 00 00 00    sub         esp,0CCh

013C4A39 53                   push        ebx

013C4A3A 56                   push        esi

013C4A3B 57                   push        edi

013C4A3C 51                   push        ecx

013C4A3D 8D BD 34 FF FF FF    lea         edi,[ebp-0CCh]

013C4A43 B9 33 00 00 00       mov         ecx,33h

013C4A48 B8 CC CC CC CC       mov         eax,0CCCCCCCCh

013C4A4D F3 AB                rep stos    dword ptr es:[edi]

013C4A4F 59                   pop         ecx

013C4A50 89 4D F8             mov         dword ptr [this],ecx

//每个实例都有this,寄存器ECX里面的值作为this指针地址

75:         return  0.30*m_r + 0.59*m_g + 0.11*m_b+1;

013C4A53 8B 45 F8             mov         eax,dword ptr [this]  //调其实例

013C4A56 F2 0F 2A 40 04       cvtsi2sd    xmm0,dword ptr [eax+4]

013C4A5B F2 0F 59 05 60 DC 3C 01 mulsd       xmm0,mmword ptr ds:[13CDC60h]

013C4A63 8B 4D F8             mov         ecx,dword ptr [this]

013C4A66 F2 0F 2A 49 08       cvtsi2sd    xmm1,dword ptr [ecx+8]

013C4A6B F2 0F 59 0D C8 DC 3C 01 mulsd       xmm1,mmword ptr ds:[13CDCC8h]

013C4A73 F2 0F 58 C1          addsd       xmm0,xmm1

75:         return  0.30*m_r + 0.59*m_g + 0.11*m_b+1;

013C4A77 8B 55 F8             mov         edx,dword ptr [this]   //调其实例

013C4A7A F2 0F 2A 4A 0C       cvtsi2sd    xmm1,dword ptr [edx+0Ch]

013C4A7F F2 0F 59 0D B0 DB 3C 01 mulsd       xmm1,mmword ptr ds:[13CDBB0h]

013C4A87 F2 0F 58 C1          addsd       xmm0,xmm1

013C4A8B F2 0F 58 05 98 DD 3C 01 addsd       xmm0,mmword ptr ds:[13CDD98h]

013C4A93 F2 0F 2C C0          cvttsd2si   eax,xmm0

76:     }

9、总结

A、dynamic(late ) binding和static(early) binding 的实现优缺点

B、内存布局的优缺点

C、多重继承性的角度看优缺点

D、有多少种实现方法可以实现同样的功能

时间: 2024-10-20 11:33:52

C++之旅 虚函数的相关文章

C++ 虚函数和虚继承浅析

本文针对C++里的虚函数,虚继承表现和原理进行一些简单分析,有希望对大家学习C++有所帮助.下面都是以VC2008编译器对这两种机制内部实现为例. 虚函数 以下是百度百科对于虚函数的解释: 定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数[1] 语法:virtual 函数返回类型 函数名(参数表) { 函数体 } 用途:实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数 函数声明和定义和普通的类成员函数一样,只是在返回值之前加入了关键字"vir

单继承与多继承中的虚函数表和虚函数指针

首先,我们了解一下何为单继承,何为多继承?? 单继承:一个子类只有一个直接父类. 多继承:一个子类有两个或多个直接父类. 单继承中的虚函数表分析: 示例程序: #include <iostream> using namespace std; typedef void(*FUNC)(); class Base { public: virtual void func1() { cout << "Base::func1()" << endl; } virt

C++:纯虚函数与抽象类

5.4.3 纯虚函数和抽象类 纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但是要求在派生类中根据需要对它进行定义,或仍然说明为纯虚函数. 声明纯虚函数的一般格式是: virtual 函数类型 函数名(参数表)=0: 纯虚函数的作用是:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行重新定义.纯虚函数没有函数体,它最后面“=0 ”并不表示函数的返回值是0,它只是形式上的作用,告诉编译系统这是纯虚函数.纯虚函数不具有函数的功能,不能被调用. //应用举例 #includ

含有虚函数的类sizeof大小

#include <iostream> using namespace std; class Base1{ virtual void fun1(){} virtual void fun11(){} public: virtual ~Base1(); }; class Base2{ virtual void fun2(){} }; class DerivedFromOne: public Base2 { virtual void fun2(){} virtual void fun22(){} }

C++沉思录之二——虚函数使用的时机

虚函数使用的时机 为什么虚函数不总是适用? 1. 虚函数有事会带来很大的消耗: 2. 虚函数不总是提供所需的行为: 3. 当我们不考虑继承当前类时,不必使用虚函数. 必须使用虚函数的情况: 1. 当你想删除一个表面上指向基类对象,实际却是指向派生类对象的指针,就需要虚析构函数. C++沉思录之二--虚函数使用的时机,布布扣,bubuko.com

C++中virtual(虚函数)的用法

转载:http://blog.csdn.net/foreverhuylee/article/details/34107615 在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念. 什么是虚函数: 虚函数是指一个类中你希望重载的成员函数 ,当你用一个  基类指针或引用   指向一个继承类对象的时候,调用一个虚函数时, 实际调用的是继承类的版本. --摘自MSDN                                                 

C++抽象类与纯虚函数实战举例

虚函数与纯虚函数请参考:http://blog.csdn.net/hackbuteer1/article/details/7558868 #include <iostream> using namespace std; class Animal { public:     virtual void eat()=0;     virtual void sleep()=0; }; class Cat:public Animal { public:     void eat()     {     

c++ 虚函数详解

下面是对C++的虚函数的理解. 一,定义 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略.下面来看一段简单的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 classA { publi

多态&amp;虚函数

(1).对象类型: a.静态类型:对象声明时的类型,编译的时候确定 b.动态类型:对象的类型是运行时才能确定的 class A {}; class B:public A {}; int main() { B* b; A* a=b;//a的静态类型是A*,动态类型(运行时)类型是B* return 0; } (2).多态 a.静态多态:函数重载.泛性编程 int Add(int a,int b) { return a+b; } float Add(float a,float b) { return