吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4086|回复: 8
收起左侧

[C&C++ 原创] C使用C++对象

[复制链接]
lovingxiaobing 发表于 2018-10-10 21:58
今天蹲茅草坑没有玩手机,后悔怎么就那么急,怎么就忘记拿手机了,,,,

看着一堆杂草胡思乱想,突发奇想:C++能使用抽象类访问动态库里里面的对象,那C怎么使用C++的类.
emmmmm

回到房间,瞎倒腾了一番,发现C++的类方法指针长度跟C的函数指针不一样长!!!而且还各种不兼容~~
恩??!!这闹哪样,我孤陋寡闻了,然后就去网上各种搜索,然后发现可以通过汇编的方式暴力获取地址但是兼容性不行,可读性也不行!
当然,如果你是大神.....请首小弟一拜....

经过胡乱倒腾了一番,终于,ok了!
下面简单讲一下思路吧。

当然,这个获取类方法指针的方法目前行不通,那么还有一个方法,用友元函数

众所周知,友元函数有权限访问类的所有成员,而且它的指针长度跟C的一样,经过测试,传值,调用并且获取返回值没问题,因为这只是个普通的函数。

然后就可以通过定义一个结构体把所有要提供给C的函数已函数指针的定义在结构体里面。(这个表述感觉没说错哈哈哈)
因为C和C++的结构体时的结构是对齐的

结构体如下:

typedef void * thisobj;

//声明
typedef struct myobj *obj;
typedef struct {
    //类方法
    void (*setNumA)(obj ob, double a);
    void (*setNumB)(obj ob, double b);
    double (*getNumA)(obj ob);
    double (*getNumB)(obj ob);
    double (*getSum)(obj ob);
    void (*outputNumber)(obj ob, double *p, double *q);
    void (*destory)(obj ob);
} __methods;


还差属性没弄进去,那么的话再定义一个结构用来存放属性(attribute)和方法(method)。

最终结构如下:

typedef void * thisobj;

//声明
typedef struct myobj *obj;
typedef struct {
    //类方法
    void (*setNumA)(obj ob, double a);
    void (*setNumB)(obj ob, double b);
    double (*getNumA)(obj ob);
    double (*getNumB)(obj ob);
    double (*getSum)(obj ob);
    void (*outputNumber)(obj ob, double *p, double *q);
    void (*destory)(obj ob);
} __methods;

//定义
typedef struct myobj {
    struct {
        /*属性指针*/
        double *a, *b;

    } attributes;

    //友元函数地址,由动态库提供静态地址
    const __methods *methods;
    //
    thisobj self;
} myobj, *obj;


我们把上面的结构扔进一个头文件里面,供C和C++使用,不能语法有差异,兼容性第一。

我们还有一个得注意的是:C++代码编译时由隐式的改名行为,所以我们得导出C风格的函数名字

extern "C" return-type function-name (...) {}


但是又有新的问题出现了,就是C中没有extern这个关键字,那么我们可以通过宏__cplusplus来判断当前是否为C++编译

所以我们的头文件最终长下面这个样子:

/*
* author: lovingxiaobing | 小冰哟
*/

//filename: cppobj.h

#ifndef _CPPOBJ_H_
#define _CPPOBJ_H_

typedef void * thisobj;

//声明
typedef struct myobj *obj;
typedef struct {
    //类方法
    void (*setNumA)(obj ob, double a);
    void (*setNumB)(obj ob, double b);
    double (*getNumA)(obj ob);
    double (*getNumB)(obj ob);
    double (*getSum)(obj ob);
    void (*outputNumber)(obj ob, double *p, double *q);
    void (*destory)(obj ob);
} __methods;

//定义
typedef struct myobj {
    struct {
        /*属性指针*/
        double *a, *b;

    } attributes;

    //友元函数地址,由动态库提供静态地址
    const __methods *methods;
    //
    thisobj self;
} myobj, *obj;

#ifdef __cplusplus
extern "C" {
#endif

obj createSimpleObject(double a, double b);

#ifdef __cplusplus
}
#endif

#endif


我们下面会写一个C++代码,最后会编译成动态链接库的形式
我们把友元函数的地址跟一个结构体绑定,这样子就可以访问了。
我们把关于某个类的的方法(友元函数)写进一个命名空间,这样子就不会混淆了
我们通过malloc函数动态申请一个结构体,然后释放结构和释放对象写进一个destory函数里
我们会发现,不能单纯的调用,得传一个实例指针进去,不然函数不知道要处理哪个实例对象,就类似与windows编程中的各种句柄,小冰猜这些个句柄应该是某个对象实例或者某个结构体的指针
我们也借鉴这种方法。
我们对于对象属性的操作,可以通过指针操作,真没白学,,,亲测可用。
我们....

由destory那么肯定得有createXXX函数,因为C没有对象。。。。

我们在createXXX函数里面处理结构的动态申请,还有对象实例化。

下面就把带有少量注释的C++代码贴上来吧。

/*
* author: lovingxiaobing | 小冰哟
*/

//filename: cppobj.cpp

#include <iostream>
#include <cstdlib>
#include "cppobj.h"

//隐藏
#define __SECRET__ __attribute__((visibility("hidden")))
//可见
#define __CANSEE__ __attribute__((visibility("default")))

namespace MYOPS
{
    class myops {
        public:
            myops(obj ob, double a, double b);
            ~myops(void);

            /* 友元函数有权限访问类所有的成员 */
            friend void setNumA(obj ob, double a);  //设置a
            friend void setNumB(obj ob, double b);  //设置b

            friend double getNumA(obj ob);    //获取a
            friend double getNumB(obj ob);    //获取b

            friend double getSum(obj ob);  //求 a + b 的值

            //通过指针导出a 和 b 的值
            friend void outputNumber(obj ob, double *p, double *q);
        private:
            double a, b;
        protected:
            obj ob;
    };

    myops::myops(obj ob, double a, double b):
                 ob(ob),      a(a),      b(b)
    {
        //利用构造函数的权限获取a和b的指针
        ob->attributes.a = &(this->a);
        ob->attributes.b = &(this->b);
    }

    myops::~myops(void)
    {
        //释放结构
        free(this->ob);
        std::cout << "(In C++ obj): struct myobj was free!" << std::endl;
    }

//隐藏这些函数符号
#pragma GCC visibility push(hidden)
    ///////////////////////////////////////
    void setNumA(obj ob, double a)
    {
        ((myops *)(ob->self))->a = a;
    }
    //////////////////////////////////////
    void setNumB(obj ob, double b)
    {
        ((myops *)(ob->self))->b = b;
    }
    //////////////////////////////////////
    double getNumA(obj ob)
    {
        return ((myops *)(ob->self))->a;
    }
    //////////////////////////////////////
    double getNumB(obj ob)
    {
        return ((myops *)(ob->self))->b;
    }
    //////////////////////////////////////
    double getSum(obj ob)
    {
        myops *o = (myops *)(ob->self);
        return o->a + o->b;
    }
    ///////////////////////////////////////////////////
    void outputNumber(obj ob, double *p, double *q)
    {
        myops *o = (myops *)(ob->self);

        *p = o->a;
        *q = o->b;
    }
#pragma GCC visibility pop    
//end,效果同__SECRET__
    ///////////////////////////////////////////////////
    __SECRET__ void destory(obj ob)
    {
        delete (myops *)(ob->self);
        std::cout << "(In C++ func. destory): object myops was deleted!" << std::endl;
    }

    //GCC 扩展的结构体初始化 (c++ style)
    static const __methods staticFriendAddressTable = {
        setNumA      : &setNumA,
        setNumB      : &setNumB,
        getNumA      : &getNumA,
        getNumB      : &getNumB,
        getSum       : &getSum,
        outputNumber : &outputNumber,
        destory      : &destory
    };

    //c style
    /*
    struct-type struct-name = {
        .name1 = value1,
        .name2 = value2,
        ......
    }
    */

} //end of namespace MYOPS

__CANSEE__ obj createSimpleObject(double a, double b)
{
    obj p = NULL;
    MYOPS::myops *o = NULL;

    if (!(p = (obj)malloc(sizeof(myobj))))
        return NULL;

    try {
        o = new MYOPS::myops(p, a, b);
    } catch (void *e) {
        free(p);
        return NULL;
    }

    p->methods = &MYOPS::staticFriendAddressTable;
    p->self = (thisobj)o;

    //返回实例结构
    return p;
}


考虑到隐私问题,我们给要隐藏的函数名用宏的形式告诉编译器不要写进符号表。做法在代码注释中。

ok,咱们编译他们:
g++ cppobj.cpp -o cppobj.so -shared  -fPIC -Os


回车,OK没有编译错误,因为我这里用的是Linux系统,所以动态链接库的后缀名是 .so
最后我们会得到一个cppobj.so的动态链接库。windows下是cppobj.dll,把上名的命令中的-o后面的改成cppobj.dll就行。


看来快要成功啦!!!!

赶快写一个C程序测试一下吧,下面是测试代码:

/*
* author: lovingxiaobing | 小冰哟
*/

//filename: callobj.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "cppobj.h"

int main(void)
{
    obj o = NULL;

    double a, b;

    printf("输入 a,b: ");
    scanf("%lf,%lf", &a, &b);

    //获取实例化对象
    if ((o = createSimpleObject(a, b)) == NULL) {
        perror("Create Error");
        exit(-1);
    }

    ///////////////////////////////////////////
    puts("测试函数调用并返回值:");
    printf("sum(a: %lf, b: %lf) = %lf\n", \
            o->methods->getNumA(o),       \
            o->methods->getNumB(o),       \
            o->methods->getSum(o));

    ///////////////////////////////////////////
    puts("通过函数设置对象属性:");
    printf("输入 a,b: ");
    scanf("%lf,%lf", &a, &b);

    o->methods->setNumA(o, a);
    o->methods->setNumB(o, b);

    printf("sum(a: %lf, b: %lf) = %lf\n", \
            o->methods->getNumA(o),       \
            o->methods->getNumB(o),       \
            o->methods->getSum(o));

    ///////////////////////////////////////////
    puts("通过指针获取或改变对象的属性:");

    printf("输入 a,b: ");
    scanf("%lf,%lf", &a, &b);

    *(o->attributes.a) = a;
    *(o->attributes.b) = b;

    printf("sum(a: %lf, b: %lf) = %lf\n", \
            *(o->attributes.a),           \
            *(o->attributes.a),           \
            o->methods->getSum(o));

    puts("-------------- c++动态库内访问c指针 --------------");
    //先清零
    b = a = 0;

    o->methods->outputNumber(o, &a, &b);
    printf("此时对象的属性:a = %lf\tb = %lf\n", a, b);
    printf("结果: a = %lf\tb = %lf\n", a, b);

    puts("----------------- free & delete ------------------");
    o->methods->destory(o); //释放结构和对象
    puts("(In C): 正常释放!");

    return 0;
}


编译再链接然后就可以得到最终的程序啦!!

gcc callobj.c cppobj.so -o callobj -Wl,-rpath=. -O2


上面的-Wl,-rpath=.告诉编译器那个动态链接库在执行文件的当前路径中,加载动态库时就在这查找。


然后运行callobj就得到如下结果:


xiaobing@xiaobing-ThinkPad-T60p:~/mycode/dong$ ./callobj
输入 a,b: 3,6
测试函数调用并返回值:
sum(a: 3.000000, b: 6.000000) = 9.000000
通过函数设置对象属性:
输入 a,b: 8,5
sum(a: 8.000000, b: 5.000000) = 13.000000
通过指针获取或改变对象的属性:
输入 a,b: 11,22
sum(a: 11.000000, b: 11.000000) = 33.000000
-------------- c++动态库内访问c指针 --------------
此时对象的属性:a = 11.000000        22.000000
结果: a = 11.000000        22.000000
----------------- free & delete ------------------
(In C++ obj): struct myobj was free!
(In C++ func. destory): object myops was deleted!
(In C): 正常释放!


通过这个结果就知道,我们成功啦!
指针操作类属性,调用友元函数,以及对象的正常释放。。。

下会把源代码上传到这,还写了一个makefile。

好像浏览器耍性子了,附件不能上传。那就网盘链接吧,这个源代码还有编译过程都已经全部贴出了,无关紧要,更重要的是你们学到了点东西。

网盘链接:https://pan.baidu.com/s/1a45nwfi8QoL_Ajt66JPLIQ

小甲鱼:change world by programming.

免费评分

参与人数 5吾爱币 +7 热心值 +5 收起 理由
wushaominkk + 3 + 1 很详细的分析!
cntank01 + 1 + 1 谢谢@Thanks!
旋木过流年 + 1 + 1 谢谢@Thanks!
nomoretime + 1 + 1 我很赞同!
15237633705 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

15237633705 发表于 2018-10-10 22:13
谢谢分享我模仿一下
iamshy520 发表于 2018-10-10 22:14
wangqiustc 发表于 2018-10-10 23:01
旋木过流年 发表于 2018-10-10 23:10
学习学习
erices 发表于 2018-10-16 00:57 来自手机
长见识了,lz的编程功底真是厉害/:strong
单程线 发表于 2018-10-22 18:41 来自手机
看不懂,我是来评论区看看有没有人和我一样的
wushaominkk 发表于 2018-10-22 19:31
很详细的分析,加油,期待你更好的作品!
D1O 发表于 2018-10-23 21:26
支持冰冰
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 19:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表