c++ - C++ 类与 C 不同文件
问题描述
目前在一个项目中,有一组明确的功能与明确的职责相关。因为责任是一个周期性的过程,每次迭代都需要缓冲区和计数器,所以我必须在每个循环之后重置这些函数使用的变量。这些变量大多是静态的,因为每个周期函数被调用数千次。(它是一组数字 FIR 滤波器,可处理每 2 分钟左右传入的 5 秒数据)。变量必须在文件范围内声明,因为函数共享它们。例如重置/初始化功能和实际的过滤功能。
到目前为止,整个项目都是用 C 语言编写的(但很容易支持 C++,因为可能破坏的部分已经包含“extern C {}”)。为了使代码更简洁,我想将函数和变量分组到一个单独的实现文件中。但当然,我也可以使用 C++ 类,我想使用更多。
这些选项之间的本质区别是什么?
我的意思的简化示例: 在这个示例中,我只是保留了程序的结构。例如,第一次迭代在 5 秒内调用了 Filter() 函数 1000 次。然后对于下一次迭代,在实际调用 Filter() 函数之前调用 Reset() 函数,以重置所有使用的缓冲区。
// File-scope variables
static float buffer[BUFFER_SIZE];
static uint8_t bufferOffset = 0;
// Filter
static float Filter (const float sample)
{
buffer[bufferOffset] = sample;
// Removed actual filter code here
return result;
}
// Reset functions
static void Reset (void)
{
memset(buffer, 0, sizeof(buffer));
bufferOffset = 0;
}
解决方案
C 中避免这些共享状态的常用方法是定义一个封装所有相关状态的结构,将其传递给每个函数并单独对其进行操作。
例子:
// buffer.h
#pragma once
// opaque data structure whose content
// isn't available to the outside
struct buffer;
// but you may allocate and free such a data structure
struct buffer *alloc_buffer();
void free_buffer(struct buffer *b);
// and you may operate on it with the following functions
float filter_buffer(struct buffer *b);
void reset_buffer(struct buffer *b);
void add_to_buffer(struct buffer *b, const float *data, size_t size);
源代码如下所示:
// buffer.c
#include "buffer.h"
struct buffer {
float content[BUFFER_SIZE];
uint8_t offset;
}
struct buffer *alloc_buffer() {
return malloc(sizeof(struct buffer));
}
void free_buffer(struct buffer *b) {
free(b);
}
float filter_buffer(struct buffer *b) {
// work with b->content and b->offset instead
// on global buffer and bufferOffset
return result;
}
void reset_buffer(struct buffer *b) {
memset(b->content, 0, BUFFER_SIZE);
b->offset = 0;
}
void add_to_buffer(struct buffer *b, const float *data, size_t num) {
memcpy(b->content + b->offset, data, sizeof(float) * num);
b->offset += num;
}
因此,您可以避免全局状态,例如显着简化代码的多线程应用程序。而且由于您返回一个不透明的数据结构,您可以避免泄漏有关缓冲区内部结构的信息。
现在你可以在不同的源文件中使用这个数据结构:
#include "buffer.h"
int main() {
struct buffer *const b = alloc_buffer();
// b->content[0] = 1; // <-- error, buffer is an opaque data type and
// you may only use the functions declared in
// buffer.h to access and modify it
const float data[2] = { 3.1415926, 2.71828 }
add_to_buffer(b, data, sizeof(data) / sizeof(data[0]));
const float result = filter_buffer(b);
return 0;
}
回答你的问题:即使你可以将你的函数和全局状态进一步分成几个编译单元,最终你仍然有一个共享的全局状态。除了在某些特殊情况下,我认为这是代码异味。
上述解决方案或多或少对应于C++解决方案。你定义了一个类,封装了一些状态和对其进行操作的方法。所有实例化的对象都是相互独立的。
推荐阅读
- react-native - GetStream Chat React 本机:[错误:不支持 crypto.getRandomValues()。见 https://github.com/uuidjs/uuid#getrandomvalues-not-supported]
- javascript - 按给定顺序对数据表的列进行排序
- ruby-on-rails - Docker-compose Rails 后端 + postgresql 无法将主机名“db”转换为地址:名称或服务未知
- python - python函数输入参数中的空格,因为它是一个cython函数?
- jenkins - 在多分支管道中禁用分支并使用扫描事件重新打开
- javascript - 查找具有重复项的两个数组中的差异
- java - Spring - 根据数据库决定配置文件
- microsoft-graph-api - 使用 Bot 向 Microsoft Teams 会议发送消息
- html - 如何导出 pdf 或发送包含包含边框的 html 的电子邮件?
- react-native - 在设置 redux 状态后呈现经过身份验证和未经身份验证的条件堆栈导航器