嵌入式中如何把C++代码改写成C语言代码

      由于C++解释器比C语言解释器占用的存储空间要大500k左右。为了节省有限的存储空间、降低成本,同时也为了提高效率,将用C++语言写的源程序用C语言改写是很有必要的。

C++与C最大的区别,就是C++中的类的概念和特性,将C++改为C的问题,就转换成如何将类化去的问题。

方法有两种:

第一种是将C++中的面向对象特征去掉,先全部理解源代码的逻辑,然后改写;第二种是在C中保留面向对象的部分特征,用结构体实现类的功能。

第一种方法,对于类的数目很少的情况还可以,如果类的数目比较多,全部理解源代码,然后重写就很耗时间,而且很容易出错,更甚者,如果遇到大的项目想全部理解源代码几乎是不可能的。

如果程序中类有140多个,这个时候就需要采用第二个方法了,你可以一个类一个类的改没有什么太高的难度,如果不是笔误的话,几乎不会出错,而且根本不需要理解程序逻辑,也许改完后你对程序所要实现的功能还一无所知。倒不是说一无所知对大家有好处,只是想说这种方法的与程序逻辑本身的无关性。

下面,对C++的一些特性,以及如何在C里面实现或者替代,作一些初步的探讨。

说明:

  • 函数Ixx为类xx的构造函数的实现

  • 原类的成员函数改为前缀为结构体名+‘_’的函数

  • 函数指针U为原类的析构函数的声明

  • U+结构体名称为原类的析构函数的实现

  • Fun-_+结构体名为对该结构体成员函数指针进行指向

  • 以后遇到上述情况将不再说明

类的成员函数和数据成员

由于struct没有对成员的访问权限进行控制,必须加入额外的机制进行访问控制,这样一来就使得程序复杂化了,所以只能放弃访问权限的控制。

1)对于类的数据成员可以直接转为C中结构体的数据成员。

2)函数则需转化为对应的函数指针,因为struct里不允许出现函数的声明和定义。而函数前如果有virture,inline等修饰符也要去掉,如函数void funca(int a);改为void (*funca)(struct B *p,int a);大家可以看到函数指针的原型里加了一个指针struct B的指针,这是因为要在函数内部对类的成员进行操作,要靠该指针指定结构体的成员。在类的成员函数里,实际上在参数列里也隐含有一个指向自身的this指针。

3)对于静态成员则要定义成全局变量或全局函数,因为结构体中不能有静态成员。

类的构造函数

类在实例化的时候会调用类的缺省构造函数,在struct里,要定义一个同名函数指针指向一个具有构造函数功能的初始化函数,与构造函数不同的是,要在初始化函数里加入进行函数指针初始化的语句。使用的时候,在创建结构体变量的时候要用malloc而不是new,并且这个时候要手工调用初始化函数。

如下例所示:

class A{public:    A();    ~A(); void func(int a); private: int b;};A::A(){    b=0;}

void A::func(int a){    b=a;}

typedef struct classA A;struct classA{ void (*A)(struct classA *p);//构造函数指针 void (*U)(struct classA *p);//析构函数指针 void (*func)(struct classA *p,int a); int b;};

void fun_A(A *p){     p->func=classA_func; //将函数指针初始化}

void IA(A *p) //构造函数,命名规则在类名前加I{     fun_A(p);     p->b=0;    //原构造函数所作部分}

void classA_func(A *p,int a){    p->b=a;}

在使用的地方采用如下方式:

  A *s=(A*)malloc(sizeof(A));  s->A=IA;  s->A(s);

类的析构函数

类的析构函数所做的工作是释放所占的资源。

在C中,无论是哪个struct都用函数指针U替代析构函数。之所以所有的struct都用指针U是基于如下情况:

如果将子类指针赋给基类指针,基类指针在释放的时候不必考虑调用哪个函数名的析构函数,只需调用成员函数U即可。成员函数U需要像一般成员函数一样在fun_类名()函数中指定。

类的析构函数是由系统调用的,在C中则要显式调用。至于何时调用,要准确判断。

类的拷贝构造函数

类的拷贝构造函数主要用途是加快以下情况下类的构建速度:

1. 作为参数传给函数。(additem(Itema))

2. 作为函数返回值。

3. 实例化类时作参数。

这三种情况下,都是由系统直接调用类的拷贝构造函数,而不是构造函数。

注意:C=D;不会调用拷贝构造函数,这种情况下使用的是重载‘=’运算符的方法。(详见运算符重载)

由于C中定义struct变量的时候,使用的全部是指针,不会用到拷贝构造函数,所以暂不考虑。对于原来函数参数或者返回值需要类变量的,要全部转化为类指针的方式。实例化类时作参数的情况,可以通过另外定义一个带参数的构造函数来解决。

类的内联函数和虚函数

内联函数和虚函数的修饰符inline、virture要全部去掉。内联函数体则要去掉,将内联函数在外面定义成一个函数。如下:

class B{  ...  virtual void funb();  inline int add() const {return a+b;}; private:   int a;   int b;   ...}

改为:

typedef classB B;struct classB{    … void (*funb)(struct classB *p); int (*add)(struct classB *p); int a; int b;}

void classB_funb(B *p){     …}

int classB_add(B *p){ return p->a+p->b;}

void fun_classB(B *p){     …     p->funb=classB_funb;     p->add= classB_add;}

重载

类中重载有函数重载和运算符重载两种:

1)函数的重载

函数重载满足的条件是:函数名相同,参数个数或者参数类型不同。

这样在调用的时候,会根据你输入的参数不同,调用不同的函数。

在C中只好分别起不同的名字,没有别的解决办法。

2)运算符重载

运算符重载只是为了满足一般的运算符使用的习惯而又不会出现错误。

C中不支持运算符重载,可以定义一个函数实现该功能。

这是一般类的修改。

七、类的继承

1)单继承
如果类之间有继承关系,先将基类按照一般类的改法,修改好,然后将基类的定义部分全部拷到子类的前头。

除了将基类的构造函数名改为子类构造函数名外,不可以将基类定义的部分作其他改动。并在构造函数里调用基类的构造函数,然后如果子类覆盖了基类的函数,则要把该函数指针重定向到子类函数。这是为了保持类的继承带来的动态联编的特性。

类之间的继承关系是复杂且多变的,为了保证基类在所有子类中的唯一而且方便修改,最好的方法就是把基类的结构体部分做成宏,在子类中直接使用即可。

2)多继承
我个人认为多继承是最好不要用,他会带来一些问题,会出现多个继承路径的问题。除非是为了方便编程而使用的,如继承接口等等。
多继承也是可以改的,将多个基类的成员全部拷到子类里,遇到重复的成员名,则在前面加上前缀来区别,当然这个指的是基类之间有相同的,如果是派生类和基类之间有重名的,则会覆盖基类。

其他

以上就是C++中主要的与C的区别最大而且最常用的特性及修改方法。

其他的还有一些比如模板的使用等等,这些都是为了方便编程,复用代码。C语言中没有,只好自己写多个函数来分别实现。

另外,还有参数列表里的&符号要用指针替代,缺省值也要去掉,而在调用的时候要注意将缺省值写上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131376.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis-使用java代码操作Redis->java连接上redis,java操作redis的常见类型数据存储,redis中的项目应用

java连接上redisjava操作redis的常见类型数据存储redis中的项目应用 1.java连接上redis package com.zlj.ssm.redis;import redis.clients.jedis.Jedis;/*** author zlj* create 2023-11-03 19:27*/ public class Demo1 {public static void main(String[] args) { // …

【JavaSE】基础笔记 - 类和对象(上)

目录 1、面向对象的初步认知 1.1、什么是面向对象 1.2、面向对象与面向过程 2. 类定义和使用 2.1、简单认识类 2.2、类的定义格式 2.3、自定义类举例说明 2.3.1、定义一个狗类 2.3.2、定义一个学生类 3、类的实例化 3.1、什么是实例化 3.2、类和对象的说明 1、面向…

非线性【SVM】的创建和使用

先来绘制散点图: from sklearn.datasets import make_circles X,y make_circles(100, factor0.1, noise.1) # 100个样本,factor:内圈和外圈的距离之比,noise:噪声 X.shape y.shape plt.scatter(X[:,0],X[:,1],cy,s50,cmap"rainbow&qu…

园区网真实详细配置大全案例

实现要求: 1、只允许行政部电脑对全网telnet管理 2、所有dhcp都在核心 3、wifi用户只能上外网,不能访问局域网其它电脑 4、所有交换机上开rstp协议,接入交换机上都开bpdu保护,核心lsw1设置为根桥 5、只允许vlan 10-40上网 5、所有…

MFC 窗体插入图片

1.制作BMP图像1.bmp 放到res文件夹下,资源视图界面导入res文件夹下的1.bmp 2.添加控件 控件类型修改为Bitmap 图像,选择IDB_BITMAP1 3.效果

【触想智能】工业显示器上市前的检测项目分享

工业显示器在上市前,需要做一项重要的工作,那就是工业显示器出厂前的产品可靠性检测。 工业显示器选择的测试项目相比商用端更为严格,常见的性能测试项目包括高温老化、防尘防水、电磁静电干扰、防摔防撞等,在工业级应用领域&…

学习Opencv(蝴蝶书/C++)相关——1. 前言 和 第1章.概述

文章目录 1. 整体架构1.1 OpenCV3.01.2 Opencv4.xX. 在线文档X.1 Opencv cheatsheet(小抄)1. 整体架构 1.1 OpenCV3.0 对于Opencv3.x版本,网上最常见的图,图自OpenCV Tutorial-Itseez 现在已经不是500+的算法了,而是2500+,详见:About

CANoe新建XML自动化Test Modules

文章目录 1.打开Test Modules2.新建Environment3.新建XML Test Modules4.新建.can文件5.打开XML Test Modules6.新建xml脚本并保存7.编译8.在.can文件写个测试用例9.修改报告格式为HTML10.运行查看报告后面介绍的文章会重复用到这部分,这里单独介绍下,后面不做重复介绍。 1.…

springboot中使用redis管理session

前言 使用软件: redis: linux版本下载 windows版本下载 安装redis 下载redis http://download.redis.io/releases/ 源码安装redis(ubuntu) #将指定版本的redis上传到服务器#解压 sudo tar -xzvf redis-6.2.4.tar.gzcd re…

python创建一个简单的flask应用

下面用python在本地和服务器上分别创建一个简单的flask应用: 1.在pc本地 1)pip flask后创建一个简单的脚本flask_demo.py from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return Hello, World!winR进入命令行,…

MongoDB设置密码

关于为什么要设置密码 公司的测试服务器MongoDB服务对外网开放的,结果这几天发现数据库被每天晚上被人清空的了,还新建了个数据库,说是要支付比特币。查了日志看到有个境外的IP登录且删除了所有的集合。所以为了安全起见,我们给m…

NLP之Bert介绍和简单示例

文章目录 1. Bert 介绍2. 代码示例2.1 代码流程 1. Bert 介绍 2. 代码示例 from transformers import AutoTokenizertokenizer AutoTokenizer.from_pretrained("bert-base-chinese") input_ids tokenizer.encode(欢迎来到Bert世界, return_tensorstf) print(input…

图形化ping工具gping

一、介绍 gping能够以折线图的方式,实时展示 ping 的结果,支持 Windows、Linux 和 macOS 操作系统。并且支持多个目标同时Ping同时展示折线图方便对比。下面扩展一下ICMP及ICMP隧道。 ICMP消息结构: ICMP消息是由一个类型字段、一个代码字段、…

台球厅桌球室计时计算软件计费方法,台球厅的电脑怎么计时

台球厅桌球室计时计算软件计费方法,台球厅的电脑怎么计时 今天给大家分享的是 佳易王桌球计时计费软件V18.0版本,只需点开始计时即可,结账的时候,软件自动计算金额。 灯控为可选,点开始计时,相应的桌灯亮…

pytorch 中 nn.Conv2d 解释

1. pytorch nn.Con2d 中填充模式 torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride1, padding0, dilation1, groups1, biasTrue, padding_mode‘zeros’, deviceNone, dtypeNone) 1.1 padding 参数的含义 首先 ,padd N, 代表的是 分别在 上下&…

Android 控件背景实现发光效果

主要实现的那种光晕效果&#xff1a;中间亮&#xff0c;四周逐渐变淡的。 这边有三种发光效果&#xff0c;先上效果图。 第一种、圆形发光体 实现代码&#xff1a;新建shape_light.xml&#xff0c;导入以下代码。使用时&#xff0c;直接给view设置为background。 <?xml …

IOS手机耗电量测试

1. 耗电量原始测试方法 1.1 方法原理&#xff1a; 根据iPhone手机右上角的电池百分比变化来计算耗电量。 1.2实际操作&#xff1a; 在iOS通用设置中打开电池百分比数值显示&#xff0c;然后操作30分钟&#xff0c;60分钟&#xff0c;90分钟&#xff0c;看开始时和结束时电池…

专业媒体播放软件Movist Pro中文

Movist Pro是一款专为Mac用户设计的专业媒体播放器。它支持广泛的视频和音频格式&#xff0c;包括MP4、AVI、MKV等&#xff0c;并提供了高级播放控件和定制的视频设置。其直观易用的用户界面&#xff0c;使得播放高清视频更为流畅&#xff0c;且不会卡顿或滞后。同时&#xff0…

Linux 将Qt程序打包为AppImage包

前言 在 Linux 环境下&#xff0c;开发完 Qt 程序后&#xff0c;也需要制作为一个安装包或者可执行文件进行分发。这里介绍使用 linuxdeployqt 将 Qt 程序打包为 .AppImage 应用程序&#xff08;类似于 Windows 的绿色免安装软件&#xff09; 环境配置 配置 Qt 环境变量 这…

链队的练习

链队的练习 相关内容&#xff1a;队列的链式存储结构&#xff08;链队&#xff09; //链队的初始化、入队、出队、取对头 #include<stdio.h> #include<malloc.h> #define OK 1 #define ERROR 0 typedef int Status; //结点结构 typedef struct QNode{ int data; …