从原始数据中填充成员的正确方法是什么

时间:2012-10-08 19:32:44

标签: c++

我有一个使用const u_char *的原始数据指针,以及像这样的通用类

class Rectangle 
{
   u_int8_t length;
   u_int8_t height;
   ...
}

假设原始数据是字节的二进制“流”,那么将原始数据放入类的字段的最佳方法是什么。

-memcpy ?
-cast   ?

可以这样做:

Rectangle *rect = (Rectangle*)rawdata;

但我知道这是一种“旧式”演员。

什么是正确的方法?

5 个答案:

答案 0 :(得分:2)

我会说有一个构造函数:

Rectangle(const u_char*)

对所有代码进行强制转换现在可以正常工作,但如果您想稍后更改您的课程,这是一个糟糕的主意。有一个构造函数可能意味着一些开销,但你有一个逻辑发生点。

如果稍后您决定要向Rectangle添加虚拟方法,则代码中的所有强制转换都将变得无用。

当然,如果您想从数据构造新对象。如果您经常序列化/反序列化对象,我会使用序列化方法:

const u_char* toUChar() const;
void fromUChar(const u_char*) const;

答案 1 :(得分:2)

Rectangle& rect = *reinterpret_cast<Rectangle*>(rawdata);

如果rawdatavoid,我会这样做,或者如果rawdata是任何其他类型(我可以直接参与其中),则直接转换为引用。

我更喜欢该引用,因为我发现它比使用原始指针更不容易出错。但是,如果你需要做指针运算,那么将它作为原始指针是没有问题的。根据您的使用情况,您可能希望转换为const Rectangle&而不是非const

但是,通常使用原始字节流,您需要发明一个协议,并且不应直接转换为结构或类。结构和类可能有填充,这会弄乱你的直接演员。无论如何,演员都会默默地成功,但你的价值观会出乎意料。协议就像......

  

0 offset:(4 bytes - float)size

     

4 offset:(2 bytes - uint16_t)height

等。执行协议方法意味着您必须逐个分配成员。

答案 2 :(得分:1)

最简单,最可靠的方法是简单地使用转换构造函数:

class Rectangle
{
public:
  Rectangle(uint8_t l, uint8_t h) : length(l), height(h) {};
  // ...
};

这应该是你的首选方法,直到无论出于何种原因这是不可能的。

除此之外,接下来要做的最好的事情就是进行成员初始化:

Rectangle rect;
rect.width = 20;
rect.height = 40;

如果无法执行上述操作,并且 iff 有问题的对象是标准所指的“聚合”(基本上是POD),则可以使用如下的初始值设定项:

Recatagle rect = {10,20};

执行此操作时,您必须记住,成员将按照在课程中声明的顺序进行初始化。如果更改声明的顺序,您将打破上面的每个初始化。这非常脆弱。出于这个原因,我将这样的构造的使用限制在有问题的类是高度本地化的情况下(比如单个翻译单元中的帮助器类),并且我记录了保持声明顺序完整的必要性。

编辑评论:

在您尝试将字符串复制到类中的实例或指向任何类型数据的指针中,您需要执行深层复制:

class Gizmo
{
public:
  Gizmo(const char* str) : str_(0)
  {
    str_ = new char[strlen(str)+1];
    strcpy(str_,str);
  }
};

注意上面代码的笨拙和脆弱程度。这里有很多可能出错的东西。当delete被摧毁时,其中最重要的是忘记了str_ Gizmonew char字符串中的unique_ptr字符串的丑陋和看似缺乏必要性第一名,一个接一个的错误...列表继续。出于这些原因,最好避免使用原始指针并使用智能指针(即shared_ptrstd::string等)或集合类。在这种情况下,我使用class Gizmo { public: Gizmo(const char* str) : str_(str) {}; private: std::string str_; }; ,可以将其视为集合类:

u_char*

随意将其转换为与{{1}}一起使用,并通过验证源指针有效来增加稳健性。

答案 3 :(得分:0)

“新风格”演员是

Rectangle *rect = reinterpret_cast< Rectangle* >( rawdata );

但你应该小心对齐,你班上的填充。

这将使用相同的内存,它只会像Rectanle对象一样解释它。

memcpy会复制它。取决于您的需求。最有可能的是,你不需要它。

答案 4 :(得分:0)

虽然直接转换速度要快一些,但这样做会将您的类锁定为特定的设计。如果您稍后对类中的数据进行更改,则所有转换都将中断。

处理原始数据的更好方法是创建构造函数和/或重载输入流操作符。我个人更喜欢将运算符重载到构造函数,因为我讨厌对一个类有多个简单的构造函数。重载可能如下所示:

istream& operator >> (istream& input, char* rawData)
{
    //fill your object's variables directly from the raw data
}

通过这种方式,您可以控制从数据中填充对象,如果数据或对象发生变化,您只需在一个位置更改代码即可在任何位置进行修复。

我也喜欢使用运算符重载而不是为它创建函数,因为我认为它使代码看起来更好更快,看你正在做什么。