C模拟多态

【问题】 多态的虚函数调用,含虚函数对象大小计算,字节对齐,函数覆盖,构造与析函数的执行顺序,This指针。

【简介】 首先,需要介绍一下用C语言实现C++的单根继承,然后分析一下构造函数和析构函数的执行顺序。看看C++都在背后做了什么,这也是C++的基本内容,不涉及哲学和C++软件的复用性讨论,立足于,C++是对C的扩展,Class是对结构体的扩展,用struct和变量的讨论,代替class和属性。

【概念】

1.继承:子类继承父类,就是被叫做子类的结构体内,含有称为父类类型的结构体变量。例:drived结构体内含有base结构类型的变量。

2.多态:在具有包含关系的结构体间的强类型转换,通过修改结构体内,指向虚表的,指针变量的内容,使其指向不同的虚表(虚表:指针函数结构体),来实现虚函数调用的功能。

【代码】 [code] #include #include

typedef void(F_BASE_A)(void *obj); typedef void(F_BASE_B)(void obj); typedef void(F_DRIVED_B)(void *obj);

void f_base_a(void *obj) { printf(“base class function a is called!\n”); }

void f_base_b(void *obj) { printf(“base class function b is called!\n”); }

void f_drived_b(void *obj) { printf(“drived class function b is called!\n”); }

typedef struct base_vt { unsigned int rtti; F_BASE_A base_a; F_BASE_B base_b; }base_vt;

typedef struct drived_vt { unsigned int rtti; F_DRIVED_B drived_b; }drived_vt;

typedef struct Base { void *vtr; int b_data; }Base;

typedef struct Drived { Base b; int s_data; }Drived;

base_vt g_base_vt; drived_vt g_drived_vt;

void complier_init() { g_base_vt.rtti = 6; g_base_vt.base_a = f_base_a; g_base_vt.base_b = f_base_b;

    g_drived_vt.rtti = 8;
    g_drived_vt.drived_b = f_drived_b; } void t_base_call() {
    printf("### test base begin ###.\n");
    Base b;
    b.vtr = &g_base_vt;


    //call stlye 1
    F_BASE_A function =  (F_BASE_A)(g_base_vt.base_a);
    function(&b);

    function = (F_BASE_B)(g_base_vt.base_b);
    function(&b);

    //call style 2
    F_BASE_A fun_a = (F_BASE_A)(( (base_vt*)b.vtr )->base_a);
    fun_a(&b);

    F_BASE_B fun_b = (F_BASE_B)(( (base_vt*)b.vtr )->base_b);
    fun_b(&b);

    //call style 3
    unsigned int *ptr = (unsigned int*)(&b);
    printf("%d\n", *ptr);

    ptr = (unsigned int*)(*ptr);
    printf("%d\n", *ptr);

    F_BASE_A fun = (F_BASE_A)(*(ptr+1));
    fun(&b);

    fun = (F_BASE_B)(*(ptr+2));
    fun(&b);
    printf("### test base end ###.\n"); }

void t_drived_call() {
printf(“### test drived begin. ###\n”); Drived d; d.b.vtr = &g_drived_vt;

    unsigned int *ptr = (unsigned int*)(&d);
    ptr = (unsigned int*)(*ptr);
    F_BASE_A function = (F_BASE_A)(*(ptr+1));
    function(&d);

    function=(F_DRIVED_B)(*(ptr+2));
    function(&d);
    printf("### test drived end. ###\n"); }

void t_poly() { printf(“### test poly begin. ###\n”);

    Drived d;
    d.b.vtr = &g_drived_vt;

    Base* base = (Base*)&d;
    unsigned int *ptr = (unsigned int*)(base);
    ptr = (unsigned int*)(*ptr);
    F_BASE_A function = (F_BASE_A)(*(ptr+1));

    function(base);

    function = (F_DRIVED_B)(*(ptr+2));
    function(base);
    printf("### test poly end. ###\n");

}

int main(int argc, char** argv) {

    complier_init();
    t_base_call();
    return 0; }

[/code]

【多态】 如果说class是struct的加强版本的话,class相比struct有了虚表的管理和对成员变量权限管理(public, protected, private).对于stuct来说,struct没有成员变量的权限管理,默认所有的struct成员变量都默认为是public属性,可以被其他函数访问。这篇主要是描述,C如何模拟C++对class虚表的模拟。

模拟虚表管理采用的方式是,用两个struct模拟class,一个struct用于存储class的数据,一个struct用于存储class中接口函数的函数指针(函数指针集合。) 【模拟类的定义】 [code] //附属结构体(虚表):用于存储,指向函数(接口)的函数指针。 typedef struct base_vt { unsigned int rtti;// 是一个存储继承信息的变量。 F_BASE_A base_a;//函数指针 F_BASE_B base_b;//函数指针 }base_vt;

//主结构体(数据):用于存储数据。 typedef struct Base { void *vtr; //1.vtr指针用于指向base_vt结构体。2.vtr一定要是struct的第一个成员变量,这是之后实现多态的关键。 int b_data; }Base; [/code]

并且这两个struct之间通过一个”void* vtr“的空类型指针进行联系。vtr是主stuct的一个指针类型的成员变量,用于指向附属类所在的内存空间。

在定义函数指针的时候,使用了自定义的宏。 [code] typedef void(F_BASE_A)(void *obj); typedef void(F_BASE_B)(void obj); typedef void(F_DRIVED_B)(void *obj); [/code]

【this指针】 在C++中,非静态的成员函数的形参列表中,有一个被隐藏的参数,就是”this“ [code] class Sample { public: void foo(int i); } [/code]

实际上foo的参数列表会被编译器翻译成,foo(Sample* this, int i); 这也是为什么,在成员函数中,可以访问Sample类的数据。

[code] class Sample { public: void foo(Sample* this, int i); } [/code] 而我们在使用函数指针宏的时候,typedef void(*F_BASE_A)(void *obj),指定了void *obj,相当于this指针,让用宏定义的这些成员函数,都可以访问主结构体的成员数据。

[code] typedef void(*F_BASE_A)(void *this) //obj和this作用类似 [/code] 【static方法】 C++类,有一种叫做static的成员函数,类的static函数,可以在类不被实例化前,允许调用,但是类的static方法不能访问类的成员变量,类没有对象实例化,在内存中就类成员的空间,也无从访问其数据。但是,如果类成员变量也是static类型的话,static函数就可以,因为static变量被分别在常量存储区。

[code] typedef void(*F_BASE_A)(void *obj); [/code] 如果把上面的函数定义的参数”void *obj“删除,那么用F_BASE_A定义的函数,就类似于static方法,因为没有obj指针,对于函数来说,是不能访问主struct的数据的。

[code] typedef void(*F_BASE_A)(); [/code]

【字节对齐】

【对象的size】

【选读】 市面上往往出现过很多的大部头书,大部头书更多的时候被当做工具书进行查阅,但是如果书的组织形式不好,就很难高效的找到自己主要想看的内容,更多的视野被重复的内容的占据,做重复的阅读工作。

参考文档: http://blog.chinaunix.net/uid-20940095-id-66146.html http://club.topsage.com/thread-2263309-1-1.html

糖果

糖果
LUA从入门到放弃

Moonscript如何显示复选框

Moonscript如何显示复选框 Continue reading

Lapis框架的常用处理方法

Published on March 02, 2017