标量删除析构函数问题

scalar deleting destructor issue

本文关键字:问题 析构函数 删除 标量      更新时间:2023-10-16

我不明白为什么下面的代码会出错。

对象A的实例将被连续地推送到vectorvectorA.push_back(A a))中。因此,有时vectorA需要重新分配;将调用析构函数,也就是A的析构函数被调用的地方,然后出现错误消息。

class A
{
    long filePos;
    union {
        Recording* recording;
        UINT64 timeStamp;
    };
public:
    inline A(long fpos, UINT64 ts) : filePos(fpos), timeStamp(ts) {}
    ~A()
    {
        if (getDetailedType() == RECORDING_TYPE)
            if (recording)
                delete recording; // error: scalar deleting destructor ???
    }
    inline short getDetailedType() const { return (short)(timeStamp % 5); }
    A(const A& edi)
    {
        filePos = edi.filePos;
        if (getDetailedType() == RECORDING_INFO)
            recording = edi.recording;
        else
            timeStamp = edi.timeStamp;
    }
}
class Recording : protected RECORDINGS
{
    UINT64 timestamp;
    float scalar;
public:
    ~Recording() // with or without this dtor, got same error
    {
    }
    inline Recording()
    {
        timestamp = 0;
        scalar = 2.0;
        time = 0;
        rate = 30;
        type = 1;
        side = 1;
    }
}
typedef struct
{
    UINT32 time;
    float rate;
    int type;
    int side;
} RECORDINGS;

您的复制构造函数执行浅层复制。因此,现在有两个对象,它们都具有相同的recording指针。

您应该进行深度复制,或者确保所有权正确转移(如果C++11可用,则使用类似std::unique_ptr<Recording>的东西。

请参阅这个关于深层副本和浅层副本之间区别的问题。

让我们看一些例子:

class ABadCopyingClass
{
public:
   ABadCopyingClass()
   {
       a_ = new int(5);
   }
   ~ABadCopyingClass()
   {
       delete a_;
   }
private:
    int* a_;   
};

上面的类很糟糕,因为默认的复制构造函数和赋值运算符将执行浅层复制,并导致两个对象都认为自己拥有底层a_对象。当其中一个超出范围时,a_将被删除,而另一个将留下一个悬空指针,最终导致崩溃。

class ABetterCopyingClass
{
public:
    ABetterCopyingClass()
       a_(new int(5))
    {
    }
    ABetterCopyingClass(const ABetterCopyingClass& r)
    {
        a_ = new int(*r.a_);
    }
    ABetterCopyingClass& operator=(const ABetterCopyingClass& r)
    {
        // in the case of reassignment...
        delete a_;
        a_ = new int(*r.a_);
        return *this;
    }
    ~ABetterCopyingClass()
    {
        delete a_;
    }
private:
    int* a_;    
};

这个类稍微改善了我们的情况(注意,在这个简单的例子中忽略了正常的错误检查)。现在,复制构造函数和赋值运算符正确地执行必要的深度复制。这里的缺点是我们必须添加大量的样板代码——很容易出错。

class ACannonicalCopyingClass
{
public:
   ACannonicalCopyingClass()
      : a_(new int(5))
   {
   }
   ACannonicalCopyingClass(ACannonicalCopyingClass&& moved_from)
   {
       a_ = std::move(moved_from.a_);
   }
private:
   std::unique_ptr<int> a_;
};

这个例子(仅限C++11)甚至更好。我们已经删除了大量的样板代码,但是这里的语义有点不同。在这种情况下,我们得到的不是深度复制,而是底层a_对象的所有权转移。

最容易实现的版本(仅限C++11)是提供底层a_对象共享所有权的版本。这是与您提供的示例最相似的版本,还有一个额外的好处,即它不会导致崩溃。

class ASharedCopyingClass
{
public:
   ASharedCopyingClass()
      : a_(std::make_shared<int>(5))
   {
   }
private:
    std::shared_ptr<int> a_;
};

这个版本可以随意复制,底层的a_对象将很乐意被引用计数。最后一个超出作用域的副本将引用计数设置为0,这将触发内存释放。

我的心理调试技能告诉我,您忘记为A实现一个副本构造函数,然后在副本被销毁时导致Recording的双重删除。

矢量的增长将触发复制-销毁对。