在用C++编程几年之后,最近我得到了一份在嵌入式领域用C编写代码的工作.

抛开C++在嵌入式领域中的错误与否的问题,C++中的一些特征/习语会让我怀念很多.举几个例子:

  • 泛型、类型安全的数据 struct (使用模板).
  • 拉伊.尤其是在具有多个返回点的函数中,例如,不必记住在每个返回点上释放互斥.
  • 通常是析构函数.也就是说,您为MyClass编写了一次d‘tor,如果MyClass实例是MyOtherClass的成员,则MyOtherClass不必显式取消初始化MyClass实例-它的d’tor是自动调用的.
  • 名称空间.

你从C++到C的经验是什么?br>

推荐答案

在做一个嵌入式项目时,我试着用全C语言工作过一次,就是受不了.它太冗长了,以至于很难读懂任何东西.此外,我喜欢我编写的针对嵌入式进行优化的容器,它们必须变得不安全得多,并且更难修复#define个块.

C++中的代码看起来像:

if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
    pktQueue.Dequeue(1);

变成:

if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
    Queue_Packet_Dequeue(pktQueue, 1);

很多人可能会说这很好,但如果你不得不在一行中进行多个"方法"调用,那就太荒谬了.C++的两行将变成C的五(由于80的字符长度限制).两者都会生成相同的代码,所以不像目标处理器那样在乎!

有一次(1995年),我try 为多处理器数据处理程序编写大量C语言.每个处理器都有自己的内存和程序.供应商提供的编译器是一个C编译器(某种HighC衍生工具),它们的库是封闭源代码的,所以我无法使用GCC来构建,它们的API的设计理念是,您的程序主要是initialize/process/terminate类型,所以处理器间通信充其量只是初级的.

我在放弃之前大约一个月,找到了一个cfront的副本,并把它存入MaFiges文件中,这样我就可以使用C++了.CWORD甚至不支持模板,但是C++代码要清晰得多.

通用的、类型安全的数据 struct (使用模板).

C最接近模板的是声明一个头文件,其中包含大量代码,如下所示:

TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ /* ... */ }

然后把它拉进go ,比如:

#define TYPE Packet
#include "Queue.h"
#undef TYPE

请注意,这不适用于复合类型(例如,没有unsigned char个队列),除非您先 Select typedef.

哦,记住,如果这个代码实际上没有在任何地方使用,那么你甚至不知道它的语法是否正确.

EDIT:还有一件事:您需要manually管理代码的实例化.如果您的"模板"代码不是all个内联函数,那么您必须加入一些控制来确保只实例化一次,这样您的链接器就不会抛出一堆"多个Foo实例"错误.

要做到这一点,您必须将非内联内容放在头文件的"Implementation"部分中:

#ifdef implementation_##TYPE

/* Non-inlines, "static members", global definitions, etc. go here. */

#endif

然后,在所有代码per template variantone位中,您必须:

#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE

此外,此实现部分需要是标准#ifndef/#define/#endif系列的outside,因为您可能会将模板头文件包含在另一个头文件中,但随后需要在.c文件中实例化.

是的,很快就会变丑.这就是为什么大多数C程序员甚至不try .

拉伊.

尤其是在具有多个返回点的函数中,例如,不必记住在每个返回点上释放互斥.

好吧,忘记你漂亮的代码,习惯所有的返回点(函数末尾除外)

TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{
    TYPE * result;
    Mutex_Lock(this->lock);
    if(this->head == this->tail)
    {
        result = 0;
        goto Queue_##TYPE##_Top_exit:;
    }

    /* Figure out `result` for real, then fall through to... */

Queue_##TYPE##_Top_exit:
    Mutex_Lock(this->lock);
    return result;
}

一般来说是析构函数.

也就是说,您为MyClass编写了一次d'tor,然后如果MyClass实例是MyOtherClass的成员,MyOtherClass不必显式地go 初始化MyClass实例——它的d'tor会自动调用.

必须以同样的方式显式处理对象构造.

名称空间.

这实际上很容易解决:只需在every个符号上添加一个前缀即可.这是我前面提到的源代码inflating 的主要原因(因为类是隐式名称空间).C组的人一直都在这样生活,很可能看不出有什么大不了的.

YMMV

C++相关问答推荐

带双指针的2D数组

你能用自己的地址声明一个C指针吗?

与unions 的未定义行为

InetPton()函数无效的IP地址

在函数中使用复合文字来初始化C语言中的变量

C:二进制搜索和二进制插入

如何将字符**传递给需要常量字符指针的常量数组的函数

为什么memcpy进入缓冲区和指向缓冲区的指针工作相同?

用gcc-msse 2编译的C程序包含AVX 1指令

在for循环中指向数组开头之前

如何在GET_STRING输入后对少数几个特定字符串进行C判断?

将 struct 数组写入二进制文件时发生Valgrind错误

对于STM32微控制器,全局偏移表.get和.Got.plt必须为零初始化

为什么会导致分段故障?(C语言中的一个程序,统计文件中某个单词的出现次数)

从Raku nativecall调用时精度不同

可以';t从A9G模块拨打电话

函数指针作为函数参数 - 应该使用 const 吗?

如何使用 VLA 语法使用 const 指针声明函数

nullptr_t 是否会 destruct 类型双关或指针转换?

在 C23 之前如何对空指针使用nullptr?