我有一个使用const u_char *的原始数据指针,以及像这样的通用类
class Rectangle
{
u_int8_t length;
u_int8_t height;
...
}
假设原始数据是字节的二进制“流”,那么将原始数据放入类的字段的最佳方法是什么。
-memcpy ?
-cast ?
我可以这样做:
Rectangle *rect = (Rectangle*)rawdata;
但我知道这是一种“旧式”演员。
什么是正确的方法?
答案 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);
如果rawdata
为void
,我会这样做,或者如果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_
Gizmo
,new
char
字符串中的unique_ptr
字符串的丑陋和看似缺乏必要性第一名,一个接一个的错误...列表继续。出于这些原因,最好避免使用原始指针并使用智能指针(即shared_ptr
,std::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
}
通过这种方式,您可以控制从数据中填充对象,如果数据或对象发生变化,您只需在一个位置更改代码即可在任何位置进行修复。
我也喜欢使用运算符重载而不是为它创建函数,因为我认为它使代码看起来更好更快,看你正在做什么。