【问题标题】:Clobbering automatic implicit objects破坏自动隐式对象
【发布时间】:2013-09-13 23:47:25
【问题描述】:

让我们假设以下情况。我有一个如下定义的隐式共享类:

class MyClass
{
public:
    MyClass (void) {
        data = new Data();
        data->refs = 1;
        data->dummy = 32;
    }

    ~MyClass (void) {
        if (--data->refs == 0)
            delete data;
    }

    MyClass (const MyClass& c) {
        ++(data = c.data)->refs;
    }

    MyClass& operator = (const MyClass& c) {
        ++(data = c.data)->refs;
        return *this;
    }

private:
    struct Data {
        int refs;
        int dummy;
    } *data;
};

这个想法是,当这个类被复制时,指向数据的内部指针被复制,并且对该数据的引用数量增加。但是,请考虑以下事项:

int main (void)
{
    MyClass c1;
    c1 = MyClass();
    c1 = MyClass();
    return 0;
}

我的理解是 MyClass 有三个实例,只有最后一个实例被释放。如果是这种情况,我可以做些什么来避免这些情况并确保 MyClass 的每个实例都得到清理?

【问题讨论】:

  • 你考虑过std::shared_ptr 吗?
  • 关注Rule of Three,你会没事的。
  • 您根本没有做的一件事是防止自我覆盖。好吧,你是,但这只是因为你的赋值运算符完全坏了。在假设使用 rhs 引用之前,它实际上是放弃了您先前构建的数据块。换句话说,这在分配时泄漏。使用std::shared_ptr<>。它是为这样的事情而设计的。
  • MyClass 的所有三个实例都被释放——它们都在堆栈上,C++ 编译器自动处理堆栈对象的释放。

标签: c++ class implementation


【解决方案1】:

在您的赋值运算符中,您需要确保在用新值覆盖其值之前清理分配给对象的值。毕竟,赋值的左边已经引用了一个值。编写赋值运算符最简单的方法是利用现有的复制构造函数和析构函数,并使用函数swap() 来交换两个对象:

MyClass& MyClass::operator = (MyClass c) {
    this->swap(c);
    return *this;
}

void MyClass::swap(MyClass& other) {
    std::swap(this->data, other.data);
}

这样,值已经复制到值c 中,并且当前值与c 持有的值交换。当c 被销毁时,引用计数会根据需要递减。

请注意,std::share_ptr<T> 已经使用一种相当奇特的机制进行引用计数:您可能希望使用标准类而不是滚动自己的类。

【讨论】:

    【解决方案2】:
    MyClass& operator = (const MyClass& c) {
        ++(data = c.data)->refs;
        return *this;
    }
    

    这个坏了。在破坏 this.data 之前,您需要减少它的引用并在引用降至零时将其删除。

    【讨论】:

      猜你喜欢
      • 2022-08-20
      • 1970-01-01
      • 2018-02-26
      • 2010-12-25
      • 1970-01-01
      • 1970-01-01
      • 2015-08-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多