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



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

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

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

-memcpy ?
-cast   ?

i可以这样做:

Rectangle *rect = (Rectangle*)rawdata;

但我知道这是一个"老式"演员阵容。

什么是正确的方法?

我想说有一个构造函数:

Rectangle(const u_char*)

现在,对代码进行全类型转换可以很好地工作,但这是一个糟糕的想法,以防你以后想更改你的类。拥有构造函数可能意味着一些开销,但你只有一个逻辑发生的地方。

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

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

const u_char* toUChar() const;
void fromUChar(const u_char*) const;
Rectangle& rect = *reinterpret_cast<Rectangle*>(rawdata);

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

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

然而,通常对于原始字节流,您需要发明一个协议,并且不应该直接转换到结构或类中。结构和类可能有填充,这会打乱直接转换。不管怎样,演员阵容都会默默地取得成功,但你的价值观会出乎意料。协议应该是。。。

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

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

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

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

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);
}
};

注意上面代码的笨拙和脆弱。这里有很多可能出错的地方。尤其是当Gizmo被破坏时,忘记了deletestr_new首先是char字符串的丑陋和似乎缺乏必要性,一个过了终点的错误。。。由于这些原因,最好避免使用原始指针,也不要使用智能指针(如unique_ptrshared_ptr等)或集合类。在这种情况下,我会使用std::string,它可以被认为是一个集合类:

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

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

"新风格"的演员阵容是

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

但你应该小心课堂上的对齐和填充。

这将使用相同的内存,只是将其解释为Rectanle对象。

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

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

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

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

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

我也喜欢使用运算符重载来为它创建函数,因为我认为这会让代码看起来更好、更快地了解你在做什么。

相关内容

最新更新