我可以将char *缓冲区转换为对象类型吗?

问题描述:

我出于好奇而不是困难问这个问题,因为我总是从你那里学习,甚至在无关的主题上。我可以将char *缓冲区转换为对象类型吗?

所以,考虑下面的方法,用C++编写,并用克++相连。这种方法工作正常,因为一切都被初始化为正确的大小。现在

extern "C" 
    { 
    void retrieveObject(int id, char * buffer) 
     { 
     Object::Object obj; 

     extractObject(id, obj); 
     memcpy(buffer, &obj, sizeof(obj)); 
     } 
    } 

// Prototype of extractObject 
const bool extractObject(const int& id, Object::Object& obj) const; 

,我想避免本地Object和使用memcpy的声明。

我试过的东西,如更换retrieveObject

void retrieveObject(int id, char * buffer) 
    { 
    // Also tried dynamic_cast and C-Style cast 
    extractObject(id, *(reinterpret_cast<Object::Object *>(buffer))); 
    } 

它编译和链接成功,但崩溃的时候了。考虑到我的缓冲区足够容纳一个Object,C++是否需要调用构造函数来“塑造”内存?是否有另一种方法来取代局部变量和memcpy?

我希望我是很清晰为你解答,谢谢提前。

+0

问题是,你为什么要这样做?这些东西在C++中几乎没有必要。 (如果您正在序列化为文件或网络通信,则不是这样。) – Thanatos 2011-01-06 10:45:36

+0

对象创建应该涉及构造函数。在构造函数中使用缓冲区。 – DumbCoder 2011-01-06 10:46:09

+0

我想知道为什么extractObject()的返回类型是`const bool`,为什么它说'const int&`是它的一个参数。有什么优势? – Nawaz 2011-01-06 10:50:42

在你的第一次努力......

void retrieveObject(int id, char * buffer) 
{ 
    Object::Object obj; 
    extractObject(id, obj); 
    memcpy(buffer, &obj, sizeof(obj)); 
} 

...你仍然有编译器创建局部变量OBJ,保证正确对准。在第二个努力...

void retrieveObject(int id, char * buffer) 
{ 
    extractObject(id, *(reinterpret_cast<Object::Object *>(buffer))); 
} 

...你很有希望编译器的缓冲区指向一个字节,适当对于一个Object :: Object。但会是吗?可能不会,因为运行时崩溃。一般来说,char *可以在任何给定字节上开始,因为更复杂的对象通常与字大小对齐,或者与其数据成员需要的最大对齐对齐。在Object :: Object内读取/写入整数,双精度值,指针等仅当内存正确对齐时才起作用 - 它取决于CPU等等,但在UNIX/Linux上,未对齐可能产生例如一个SIGBUS或SIGSEGV信号。

为了解释这一点,我们来考虑一个简单的CPU /内存架构。假设内存允许在任何给定的操作中从地址0-3,4-7或8-11等中读取4个字节(32位体系结构),但不能在地址处读取4字节卡盘1-4,2-5,3-6,5-8 ....听起来很奇怪,但这实际上是记忆的一个常见限制,所以只要接受它并考虑后果即可。如果我们想在内存中读取一个4字节的数字 - 如果它位于4个地址中的一个,我们可以在一次内存读取中读取它,否则我们必须读取两次:从一个包含部分数据,然后是包含其余的4字节区域,然后丢弃我们不想要的位,并将剩余的位重新组合到适当的位置以将32位值存入CPU寄存器/存储器。这太慢了,所以语言通常会谨慎地把我们想要的值放在内存可以在一个操作中访问它们的地方。即使CPU是按照这种期望设计的,因为它们通常具有直接在存储器中操作值的指令,而没有将它们明确地加载到寄存器中(即,甚至在组装/机器代码级别下的实现细节)。要求CPU对未对齐的数据进行操作的代码通常会导致CPU生成中断,操作系统可能会将其显示为信号。

也就是说,关于在非POD数据上使用它的安全性的其他警告也是有效的。

那么这可能有很多问题 - 首先,如果你使用本地对象,你不能只是构造它,然后在其上写一些其他实例的内存(这只适用于POD类型,因为它们不需要调用析构函数),否则你可能会发生令人讨厌的内存泄漏。

但是,这不是主要的问题 - 你曾经提供的解决方案可能或可能无法正常工作的基础上,使用的对象的类型。它可以用于简单的POD类型,它甚至可以用于更复杂的类(假设你将正确地处理构造函数/析构函数调用),但是在程序的其他部分期望对象处于原始状态位置 - 让我们说,你有一个类,有2个成员变量:

struct A { 
    int i; 
    int * pi; 
} 

在“圆周率”总是指向“我”的成员 - 如果你“的memcpy”该对象到其他位置,它很容易破裂。

+0

感谢您关注内存泄漏。我知道这很糟糕,它在90%的案例中不起作用,但就我而言,这很有效,因为我的结构足够简单。 – 2011-01-06 12:07:37

你正在做的事情是有效地序列化Object并且将正常工作当且仅当Object中的所有数据都连续存储。对于简单的对象,这将工作正常,但只要有对象包含指向其他对象的指针,就会停止工作。

在C++中,对象包含其他对象是非常普遍的。 std::string就是一个例子。 string类是引用其他地方存储的引用计数器对象的容器。所以除非你确定对象是一个简单的连续对象,否则不要这样做。

+0

这一切都是有效的,但与实际观察到的行为(崩溃)无关,这可能是由于缓冲区未对齐Object :: Object ...而引起的。 – 2011-01-06 12:00:34

你应该看看boost.serialisationboost::message_queues。 C++对象包含多个运行时特定的数据(virtual tables)。

您还应该考虑在模块之间传输它们时添加有关对象的版本信息。

找出崩溃的原因和位置,使用调试器。代码看起来不错。

如果你想避免中间对象实例,那么简单地避免它。使extractObject()返回一个指向Object的指针,并使用该指针memcpy()将其内容写入buffer

不过要小心,正如其他人所说的,如果你那么只是reinterpret_cast<>buffer回到Object如果Object不够简单,可能会破坏Object。