Reinterpret_cast vs placement new

时间:2012-07-24 16:21:41

标签: c++ placement-new reinterpret-cast

从阅读this post开始,很明显c ++中的放置新闻用于在预先分配的内存位置上调用类构造函数。

如果内存已经初始化,是新的展示位置还是reinterpret_cast更合适?

例如,假设我读取了一个原始字节流,表示来自TCP套接字的成帧消息。我把这个流放入一个framesync并检索一个代表我的类的已知大小的缓冲区,我称之为Message。我知道有两种方法可以继续。

  1. 创建一个构造函数,该构造函数接受一个告诉类不要初始化的标志。通过“不初始化”标志在缓冲区上执行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 );
    
  2. 使用reinterpret_cast

    Message* message = reinterpret_cast< Message* > ( buffer ); 
    
  3. 我相信这两者都会产生相同的结果。一个优先于另一个是更正确,更OO,更安全,更容易阅读或更好的风格?

2 个答案:

答案 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;

如果您不想使用预先存在的库,那么您可能希望将这些内容包装在您自己的框架中。

这样您就不必处理对齐问题,网络表示定义明确,可以在不同的实现之间传递,并且程序不会使用技术上未定义的行为。