【问题标题】:c++ casting char* to object?c ++将char *转换为对象?
【发布时间】:2011-03-25 17:37:59
【问题描述】:


是否可以在 C++ 中将字符数组转换为像这样的对象:

char* bytes = some bytes...
MyObject obj = (MyObject)(bytes);

?
我必须如何定义强制转换运算符?
谢谢:)

【问题讨论】:

    标签: c++ casting operator-overloading


    【解决方案1】:

    您可能想为 MyObject 定义一个构造函数:

    class MyObject {
    public:
      explicit MyObject(const char* bytes);
      ...
    };
    
    MyObject::MyObject(const char* bytes) {
      // do whatever you want to initialize "MyObject" from the byte string
    }
    

    然后你就可以使用它了:

    char* bytes = some bytes...
    MyObject obj = MyObject(bytes);  // this will work
    MyObject obj(bytes);             // so will this
    

    【讨论】:

    • 你也可以MyObject obj = bytes;。我建议在构造函数上使用explicit 关键字来防止这种情况,它可以避免一些令人惊讶的错误。
    • 如果你走这条路,别忘了三法则(en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29)。您还应该在其中添加一个析构函数和复制赋值运算符。
    • 是的,explicit 对于单参数构造函数来说是一个非常好的主意。我已经更新了我的答案。
    • 请注意std::string 的构造函数不是explicit 并且符合我们的预期。
    【解决方案2】:

    我可以在这里看到两种可能性。如果您知道代表目标类型的数据,则可以使用reinterpret_cast 将它们视为该类型的对象:

    MyObject *obj = reinterpret_cast<MyObject *>(bytes);
    

    如果要在指定内存中创建指定类型的对象,使用placement new操作符在指定地址构造对象:

    char *bytes = whatever;
    
    MyObject *obj = new(bytes) MyObject;
    

    当你使用完对象时,你不要delete它,你直接调用dtor:

    obj->~MyObject();
    

    请注意,要使其正常工作,您需要确保(如果没有别的)bytes 指向与目标类型正确对齐的数据。

    【讨论】:

    • 是的。这将在该位置构造一个新对象。如果你在那里有一个已经构造的对象(也许指针已经从MyObject * 投射而你现在只是把它投射回来?)那么 larsmans 的答案就是要走的路。
    • @T.E.D:不,不是真的。正如我编辑的答案中所述,如果您知道那里的内容真正代表了指定类型的对象,那么您想使用reinterpret_cast,而不是static_cast
    • @Jerry:我自己发现了答案中的错误并纠正了它。
    • 对。这可能是因为您碰巧知道一些其他代码已经在那里构造了一个对象,或者因为它是一个 POD 对象。
    • @T.E.D.:“因为它是一个 POD 对象”并没有太大的意义/帮助。它仍然几乎属于第一条规定——它只会成为一个 POD 对象,因为其他一些代码已经在那里构建了它。
    【解决方案3】:

    如果字节串实际上代表MyObject类型的有效对象,你可以得到一个MyObject*

    reinterpret_cast<MyObject *>(bytes)
    

    (但这不太可能起作用,除非char* 是一个指向正确构造的MyObject 的指针的结果。)

    【讨论】:

    • +1,但括号中的部分非常重要,应该删除括号。
    • 这不是UB吗?不违反严格的别名规则吗?
    • 铸造 to char * & 取消引用结果免于别名。没有相反的余地。但只要指针不在同一范围内取消引用——当然没有理由这样做——那么别名甚至不会进入等式。这与允许“往返”转换指针并确保转换A * => B * => A * 在两边保留相同的A * 的规则相同/遵循。如果一个人甚至不能在没有别名的情况下转换对象的地址,这是不可能的。别名仅指 dereferenced 指针,即 lvalues
    • "除非char* 是指向正确构造的MyObject 的指针的结果" => +1。该标准非常清楚,只有不兼容指针类型之间的reinterpret_cast 的“往返”形式具有定义的结果。其他任何东西都是 UB,并拼命希望你的编译器能做你认为它应该做的事情。将任意字节转换为某个对象的正确方法 - 假设它是可简单复制的,因为它必须是 - 是从 char 缓冲区分配一个实例和 memcpy() 到它。哎呀,好的优化器可以渲染到与演员相同的代码,但无需调用 UB
    【解决方案4】:

    我喜欢 Jerry Coffin 和 larsman 的回答,这取决于所讨论的位置是否已经有构造对象。

    不过还有一个问题。如果MyObject 的类型恰好符合POD class 的条件,那么可以像larsman 建议的那样对指针使用reintreprent 强制转换,因为该对象实际上不需要构造函数。

    我说“可能”是因为您的平台很可能对char * 和类指针使用不同的表示。不过,我用过的大多数都不会这样做。

    【讨论】:

    • 这不应该是对其中一个答案的评论吗?无论如何,地址类型无关紧要:转换将根据需要进行转换。问题是标准是否允许将任意字节(地址转换)重新解释为某些对象类型T,可能比编译器无法验证的char 具有更严格的对齐方式。我非常确信它确实不允许允许这样做——除非所讨论的char * 最初来自有效分配的T *——并且其他任何东西都是正式的UB。看到这条评论:stackoverflow.com/questions/5436092/…
    • (为后代复制评论)即使 trivially copyable 类型 - 符合标准的 POD 超集可复制到 char 数组并返回并保留相同的值 - 实际上不发出任何构造函数代码,标准仍然要求此类对象具有明确定义的生命周期。这大概意味着编译器必须看到代码说“在这块内存中创建一个struct T”,否则所有的赌注都关闭了,意味着它可以发出不安全的代码或优化尝试。正如您在这里非常模糊地推荐的那样,大多数编译器都允许任意转换,但这并不意味着它不是 UB。
    • 这必须是对所有三个的评论(这个软件真的不允许),另外我有一些额外的材料直接解决了这个问题。我承认当前的标准比我撰写这篇文章时使用的 C++11 之前的标准要好得多。
    猜你喜欢
    • 2012-07-01
    • 2013-12-31
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 1970-01-01
    相关资源
    最近更新 更多