11

我使用硬件 API 已经有很长时间了,而且我一直在使用的几乎所有 API 都有一个 C 接口。所以,很多时候我都在使用裸news、不安全的缓冲和许多用 C++ 代码包装的 C 函数。最后,C 纯代码和 C++ 纯代码之间的边界在我的脑海中被搞乱了(我不知道澄清这个边界是否有用)。

现在,由于一些新的编码风格要求,我需要将所有怀疑不安全的代码重构为用 C++ 编写的更安全的代码(假设 C++ 代码会更安全),最终目标是使用C++ 带来的工具。

因此,为了摆脱我所有的困惑,我正在寻求有关 C/C++ 几个主题的帮助。

memcpy对比std::copy

AFAIKmemcpy是 C 库中的一个函数,所以它不是 C++ 语言;另一方面std::copy是 STL 中的一个函数,所以它是纯 C++。

  • 但是,这是真的吗?毕竟,如果数据可以轻松复制,std::copy则将调用std::memcpy(到cstring标题中)。
  • memcpy将所有调用重构为std::copy调用会使代码更“纯 C++”?

memcpy毕竟,为了处理新的代码风格要求,我决定继续重构,关于memcpyand有一些问题std::copy

memcpy是类型不安全的,因为它与原始 void 指针一起工作,可以管理任何类型的指针,无论其类型如何,但同时非常灵活,std::copy缺乏确保类型安全的这种灵活性。乍一看,memcpy是使用序列化和反序列化例程的最佳选择(这确实是我的真实使用案例),例如,通过自定义串行端口库发送一些值:

void send(const std::string &value)
{
    const std::string::size_type Size(value.size());
    const std::string::size_type TotalSize(sizeof(Size) + value.size());
    unsigned char *Buffer = new unsigned char[TotalSize];
    unsigned char *Current = Buffer;

    memcpy(Current, &Size, sizeof(Size));
    Current += sizeof(Size);

    memcpy(Current, value.c_str(), Size);

    sendBuffer(Buffer, TotalSize);

    delete []Buffer;
}

上面的代码运行良好,但看起来很糟糕;我们正在摆脱std::string通过该方法访问它的内部内存的封装std::string::c_str(),我们需要处理动态内存的分配和释放,使用指针并将所有值视为无符号字符(参见下一部分),问题是:有更好的方法吗?

我试图解决上述问题的第一次尝试std::copy并不完全让我满意:

void send(const std::string &value)
{
    const std::string::size_type Size(value.size());
    const std::string::size_type TotalSize(sizeof(Size) + value.size());

    std::vector<unsigned char> Buffer(TotalSize, 0);

    std::copy(&Size, &Size + 1, Buffer.begin());
    std::copy(value.begin(), value.end(), Buffer.begin() + sizeof(Size));

    sendBuffer(Buffer.data(), TotalSize);
}

使用上述方法,内存管理不再是问题,它std::vector负责在范围末尾分配、存储和最终释放数据,但是std::copy与指针算术和迭代器算术混合的调用非常烦人,而且在最后,我毕竟忽略了调用中的std::vector封装。sendBuffer

在之前的尝试之后,我用std::stringstreams 编写了一些代码,但结果更糟,现在,我想知道是否:

  • 有一种方法可以安全地序列化对象和值,而不会破坏封装,不会过度或混淆指针/迭代器算术,也没有动态内存管理,或者这只是一个不可能的目标?(是的,我听说过boost::serialization,但现在我不允许集成它)。

和:

  • std::copy序列化/反序列化的最佳用途是什么?(如果有的话)。
  • 复制容器或数组的std::copy理由是有限的,并且将其用于原始内存是一个糟糕的选择?

alloc/freenew/deletestd::allocator

另一个大话题是内存分配。AFAIK尽管malloc/free函数来自 C,但它们并没有被禁止进入 C++ 范围。new/delete运算符来自 C++ 范围,它们不是 ANSI C。

  • 我是正确的?
  • new/delete可以在ANSI C中使用吗?

假设我需要将所有 C 风格的代码重构为 C++ 代码,我将摆脱一些遗留代码周围的所有alloc/free传播,我发现保留动态内存非常令人困惑,void 类型不携带任何有关大小的信息,因此不可能使用 void 作为类型来保留数据缓冲区:

void *Buffer = new void[100]; // <-- How many bytes is each 'void'?

由于缺少纯原始二进制数据指针,创建指向unsigned char. char为了等于元素的数量和大小。并且unsigned为了避免在数据复制过程中出现意外的有符号-无符号转换。也许这是一种常见的做法,但它是一团糟......unsigned char不是int也不float是,my_awesome_serialization_struct如果我被迫选择某种指向二进制数据的虚拟指针,我会更喜欢void *而不是unsigned char *.

因此,当我需要一个用于序列化/反序列化目的的动态缓冲区时,我无法避免这些unsigned char *东西以重构为类型安全缓冲区管理;alloc但是当我将所有/free对重构为new/delete对时,我读到了关于std::allocator.

允许以类型安全的std::allocator方式保留内存块,乍一看我敢打赌它会很有用,但我认为使用std::allocator<int>::allocateornew int或左右分配并没有太大区别,对于std::allocator<int>::deallocateand 也是如此delete int

现在,我对动态内存管理失去了兴趣,这就是我要问的原因:

  • 有一个很好的 C++ 实践涉及用于序列化/反序列化目的的动态内存管理,它授予类型安全管理?
  • 是否可以避免使用const char *用于序列化/反序列化的内存缓冲区?
  • 序列化/反序列化范围的基本原理std::allocator和用途是什么?(如果有的话)。

感谢您的关注!

4

1 回答 1

2

我的经验是,C++ 中的类型安全不仅意味着编译器会抱怨类型不匹配。这意味着您通常不必关心数据的内存布局。事实上,C++ 标准对某些数据类型的内存布局只有很少的要求。

您的序列化基于直接内存访问,因此,恐怕不会有一个简单的“纯”C++ 解决方案,尤其是没有通用的编译器/平台独立解决方案。

于 2012-10-15T14:19:42.307 回答