【问题标题】:how to use placement new to reinitialize the field?如何使用placement new 重新初始化字段?
【发布时间】:2013-12-14 08:57:41
【问题描述】:

我的课程包含字段:

private:
    OrderUpdate curOrderUpdate;

我一遍又一遍地使用它,它经常需要重新初始化:

for (int i = 0; i < entries.size(); i++) {
    auto entry = entries[i];
    new (&curOrderUpdate) OrderUpdate();
    curOrderUpdate.MDEntryID = entry.get_MDEntryID().value()[0];
            ...

我有几个问题:

  • 我可以使用变量还是必须将其更改为指针?将OrderUpdate curOrderUpdate 更改为OrderUpdate* curOrderUpdate
  • 分配是强制性的吗?我应该写curOrderUpdate = new (&amp;curOrderUpdate) OrderUpdate(); 还是只写new (&amp;curOrderUpdate) OrderUpdate() 就足够了?

【问题讨论】:

    标签: c++


    【解决方案1】:

    回答您的具体问题:

    • 是的,你可以直接存储对象(不是通过指针),所以OrderUpdae curOrderUpdate就可以了。

    • 不,您不必(实际上您不应该)分配。就地调用构造函数是正确的:`new (&curOrderUpdate) OrderUpdate()

    但是,为了使您的代码安全,您必须在重新调用构造函数之前调用析构函数。所以你会像这样改变循环:

    for (int i = 0; i < entries.size(); i++) {
        auto entry = entries[i];
        curOrderUpdate.~OrderUpdate();
        new (&curOrderUpdate) OrderUpdate();
        curOrderUpdate.MDEntryID = entry.get_MDEntryID().value()[0];
    

    然而,这种低级代码最好保留用于非常特殊情况,如联合和类似联合的结构。特别是,正如@JamesKanze 在 cmets 中指出的那样,如果构造函数抛出(并且析构函数不是微不足道的),代码就会出现严重问题 - 然后您的对象将被破坏两次。

    如果您简单地将reinitialise() 函数添加到OrderUpdate(),这将是更加更简洁的设计,它可以在不诉诸析构函数和构造函数调用的情况下做正确的事情。

    【讨论】:

    • reinitialize() 是完全正确的方法。在编写的示例中,构造函数不能抛出异常。如果是这样,则周围的作用域无法知道curOrderUpdate 已被销毁,并且您在展开时会发现自己处于双重破坏状态。
    • 恰恰相反。如果析构函数是微不足道的,则代码定义良好(但不是我想维护的东西)而无需调用析构函数。但是,如果您调用析构函数,并且由于某种原因放置 new 失败,则您有未定义的行为;使用析构函数后跟放置 new 来替换赋值是从来没有一个好主意。
    • @JamesKanze 对不起,詹姆斯。有点糊涂了。您的评论是参考答案还是参考我的评论(或两者兼而有之?)?来晚了。可能现在应该在床上,所以我提前为可能明显的问题道歉。
    • 你的答案。销毁,然后使用placement new,意味着对象处于无效状态的时间间隔。如果此时发生异常(例如在由placement new 调用的构造函数中),析构函数将在处于无效状态的对象上调用,从而导致未定义的行为。
    • @JamesKanze Heh。我没有答案(我的评论是我的),但你的评论似乎强化了我的评论(我认为)。所以我好像明白了。
    【解决方案2】:

    在同一个对象上重复调用构造函数是可疑的。正确的做法是

    curOrderUpdate = OrderUpdate();
    

    【讨论】:

    • 这会分配新对象吗?
    • 它创建一个临时 OrderUpdate 对象,将其分配给 curOrderUpdate,然后销毁该临时对象。如果您想避免创建临时对象,那么我认为最好的方法是向 OrderUpdate 添加一个clear 方法。该方法可以进行重新初始化。
    • 这是什么意义上的错误?在现有对象上使用placement new 在语法上肯定没有错,并且行为是明确定义的if 对象具有微不足道的析构函数。 (这是否是好的做法是另一个问题。)
    • @JamesKanze 这让我很吃惊。
    • @john 在 §3.8 中:“类型 T 的对象的生命周期在以下情况下结束:[...]-- 对象占用的存储空间被重用或释放。”在对象上使用放置 new 是重用。 (请注意,我认为这是一种好的编程技术。只是它在形式上是合法的。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 2014-03-21
    • 2021-08-22
    • 2016-06-26
    • 2012-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多