从阅读this post开始,很明显c ++中的放置新闻用于在预先分配的内存位置上调用类构造函数。
如果内存已经初始化,是新的展示位置还是reinterpret_cast更合适?
例如,假设我读取了一个原始字节流,表示来自TCP套接字的成帧消息。我把这个流放入一个framesync并检索一个代表我的类的已知大小的缓冲区,我称之为Message。我知道有两种方法可以继续。
创建一个构造函数,该构造函数接受一个告诉类不要初始化的标志。通过“不初始化”标志在缓冲区上执行new放置。
Message::Message( bool initialize )
{
//
// Initialize if requested
//
if( initialize )
{
Reset( );
}
}
void Message::Reset( void )
{
m_member1 = 1;
m_member2 = 2;
}
Message* message = new ( buffer ) Message( false );
使用reinterpret_cast
Message* message = reinterpret_cast< Message* > ( buffer );
我相信这两者都会产生相同的结果。一个优先于另一个是更正确,更OO,更安全,更容易阅读或更好的风格?
答案 0 :(得分:9)
唯一有意义的规则是:
如果某个类型T
的实例已已在地址a
构建,则reinterpret_cast<T*>(a)
获取指向的对象的指针已经存在。
如果某个类型T
的实例尚未在地址a
处构建,则使用placement new构建类型为T
的实例地址a
。
它们是完全不同的操作。
你需要问的问题非常非常简单:“对象是否已经存在?” 如果是,您可以访问它(通过演员表)。如果不是,那么你需要构建它(通过放置新的)
这两项行动与彼此无关。
这不是你应该选择哪一个的问题,因为他们做了不同的事情。你应该更喜欢做你想做的事。
答案 1 :(得分:0)
我不会说。
使用贴图新建并采用特殊的构造方法似乎是一种破解。一方面,该标准表示,例如,未初始化的int类成员具有“不确定值”&#39;并访问它可能&#39;导致未定义的行为。没有指定int将假定未修改的底层字节的值解释为int。我不认为在调用构造函数之前,有任何东西会阻止符合条件的实现从初始化内存为零。
为了正确定义reinterpret_cast的使用,你必须跳过一些箍,即使这样使用生成的对象也可能违反严格的别名规则。
更实际的是,如果您直接通过网络发送类的实现指定表示,那么您将依赖具有兼容布局的通信系统(兼容的表示,对齐等)。
相反,您应该进行实际的序列化和反序列化,例如使用memcpy()和ntoh()将数据从缓冲区获取到现有对象的成员中。
struct Message {
uint32_t m_member1;
uint16_t m_member2;
};
extern char *buffer;
Message m;
memcpy(&m.m_member1, buffer, sizeof m.m_member1);
m.m_member1 = ntohl(m.m_member1);
buffer += sizeof m.m_member1;
memcpy(&m.m_member2, buffer, sizeof m.m_member2);
m.m_member2 = ntohs(m.m_member2);
buffer += sizeof m.m_member2;
如果您不想使用预先存在的库,那么您可能希望将这些内容包装在您自己的框架中。
这样您就不必处理对齐问题,网络表示定义明确,可以在不同的实现之间传递,并且程序不会使用技术上未定义的行为。