2

我有一个使用 const u_char* 指向原始数据的指针,还有一个像这样的泛型类

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

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

-memcpy ?
-cast   ?

可以这样做:

Rectangle *rect = (Rectangle*)rawdata;

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

什么是正确的方法?

4

5 回答 5

2

我会说有一个构造函数:

Rectangle(const u_char*)

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

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

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

const u_char* toUChar() const;
void fromUChar(const u_char*) const;
于 2012-10-08T19:40:28.357 回答
2
Rectangle& rect = *reinterpret_cast<Rectangle*>(rawdata);

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

我更喜欢参考,因为我发现它比使用原始指针更不容易出错。但是,如果您需要进行指针运算,则将其设为原始指针是没有问题的。根据使用情况,您可能希望转换为 aconst Rectangle&而不是 non- const

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

0 偏移量:(4 字节 - 浮点数)大小

4 偏移量:(2 字节 - uint16_t)高度

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

于 2012-10-08T19:44:50.907 回答
1

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

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

这应该是您的首选方法,直到由于某种原因这是不可能的。

除此之外,下一个最好的事情就是简单地进行成员初始化:

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

如果无法执行上述操作,并且如果所讨论的对象是标准所指的“聚合”(基本上是 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_何时Gizmo被销毁,丑陋和似乎缺乏必要newchar字符串首先,一次过去的错误......名单还在继续。由于这些原因,最好完全避免使用原始指针并使用智能指针(即unique_ptr,shared_ptr等)或集合类。在这种情况下,我会使用 a std::string,它可以被认为是一个集合类:

class Gizmo
{
public:
  Gizmo(const char* str) : str_(str) {};
private:
  std::string str_;
};

随意将其转换为与 a 一起使用u_char*,并通过验证源指针是否有效来增加鲁棒性。

于 2012-10-08T19:46:08.083 回答
0

“新风格”演员阵容是

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

但是你应该小心对齐,你的类中的填充。

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

memcpy会复制它。取决于你的需要。很可能,您不需要它。

于 2012-10-08T19:37:11.107 回答
0

虽然直接铸造要快一点,但这样做会将你的类锁定到特定的设计中。如果您稍后对类中的数据进行更改,则所有强制转换都会中断。

处理原始数据的更好方法是创建构造函数和/或重载输入流运算符。我个人更喜欢将运算符重载到构造函数,因为我讨厌一个类有多个简单的构造函数。重载可能看起来像这样:

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

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

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

于 2012-10-08T19:50:04.987 回答