【问题标题】:Implementation of assignment for classes班级分配的实施
【发布时间】:2015-01-31 14:15:57
【问题描述】:

所以我在浏览一段 C++ 文本时遇到了以下代码:

class example
{
    int dataMember;

public:
    example& assign(const example& source)
    {
        if(this!=&source)
        {
            this->~example();
            new (this) example(source);
        }


    }

};

好的,所以我正在尝试解码此函数分配的功能。我还明白了什么:

  1. 该函数接受类实例的常量引用并返回对该类的引用。

  2. if块内,首先为当前实例调用析构函数(据我所知,当前对象被销毁并释放内存)。

现在主要问题:

new (this) example(source)

这条线让我很困扰。这里发生了什么? 如果让我猜测,我会说正在创建一个新对象并将其分配为当前对象,正如我可以从 this 关键字推断的那样。

谁能解决这个问题?这里到底是怎么回事?

这种方法安全吗? (如果分配是动态发生的,程序员将来必须手动解除分配)

谢谢。

【问题讨论】:

  • @bolov,太棒了,这正是我要找的东西。现在我将在线搜索并阅读更多相关信息。
  • 这是一个可怕的代码。
  • @bolov,该对象可能没有静态生命周期。这个对象可以在任何地方,有任何生命周期。寿命不是这里的问题。
  • @AaronMcDaid 是的,你是对的,我应该说这个 new 不会分配任何内存,因此它不会添加新删除的需要。

标签: c++


【解决方案1】:

您看到的是代码重用的尝试。这个想法是使用复制构造函数来实现赋值运算符(或者,在这种情况下是一个赋值函数)。

首先,摆脱简单的东西:

  • if 可确保正确处理自分配(例如 x.assign(x))。这是必要的,因为实现依赖于更改*this 不会更改source 的事实。通过将this 与另一个对象的地址进行比较,这确实会测试对象是否相等,但是否相同。

  • 该函数缺少return *this;,但您可能已经注意到了。

现在到剩下的两行:

this->~example();

显式调用类example的析构函数。在该行之后,this 指针不再指向对象,而是指向 sizeof(example) 的未初始化内存。

new (this) example(source);

就是所谓的placement new,它分配内存,只是简单的在this所指向的位置通过调用它的拷贝构造函数来创建一个新的example。在某种程度上,这是显式调用构造函数而不分配任何内存的语法。

请注意,这会重用 *this 之前持有的内存,无论它在哪里:它甚至可以是例如virtual 动态分配数组元素的基数...

关于安全性:在实践中,只要构造函数和析构函数都不抛出异常,这是安全的(虽然丑陋且可能效率不高)。但是,当从一个试图像这样偷偷摸摸的类派生时,您可能会遇到问题。

如果您想知道编写重用复制构造函数的赋值运算符/函数的最简单方法,请查看this question,它基本上可以归结为:

example& assign(example source)
{
    swap(*this, source);
    return *this;
}

最重要的区别:

  • 它在适用的情况下使用移动构造函数。
  • 如果复制/移动构造函数和swap 是异常安全的(它们应该始终是),则此代码是异常安全的。
  • 自分配的效率略低,因为它会执行复制,而不是识别自分配。

【讨论】:

  • 您能否扩展您上一条关于派生类的评论?您是指调用 this 中的析构函数,还是调用placement-new 构造函数?还是两者兼而有之?
  • @AaronMcDaid 新的位置将slice,如果析构函数是virtual,那么您将破坏比您预期的更多。此外,它可能共享了@98​​7654342@ 基类...
  • @AaronMcDaid 基本上:我不会依赖从此类派生的代码,无论它是否有效。 (这只适用于从它派生,而不是使用组合。)
  • 理解并同意在严肃的代码中使用(原样)风险太大。但现在我很好奇如何解决它!这个类被称为example。如果我们有一个class derived : public example,并且如果析构函数是virtual,那么这里对析构函数的调用将破坏所有derived对象,而放置new只会重建对象的一部分。这将使derived *x 指向的对象处于混乱状态。但是,是否可以在正确的切片上调用析构函数,以某种方式绕过其 virtual 特性?
  • @AaronMcDaid 是的 - 只是不要...... :)
【解决方案2】:

这部分破坏了对象:

this->~example();

它不会释放内存,但由于调用析构函数而释放的内存除外。然后,它继续调用“placement new”:

new (this) example(source);

这将在前一个对象的(未释放的)内存上构造一个对象。

关于这个的安全性,至少是危险的。如果在销毁对象后您未能创建新对象,那么您有一个奇怪的僵尸对象,当它的正常生命周期结束时,它将被第二次销毁(导致未定义的行为)。

【讨论】:

  • 我是否必须显式调用析构函数以确保没有内存泄漏?
  • 没有。通常,作用域创建一个对构造函数的调用和一个对析构函数的调用。现在,这段代码注入了一个析构函数调用,然后是一个构造函数调用。总之,您有两个构造函数/析构函数对。
猜你喜欢
  • 2014-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
相关资源
最近更新 更多