在c++中的赋值运算符重载方法中删除旧的动态分配内存



我有两个相互关联的问题。

我正在实现一个区域四叉树结构。在这个QTree类中,我有一些字段,更重要的是一个

vector<QTree*> quads; 

包含当前正方形的四个分割区域的字段。首先,我想为这个类实现一个析构函数。注意,我不能在课堂上定义公共领域的任何东西(如果我是,这只是小菜一碟!)所以我做了这个:

QTree::~QTree ()
{
if ( quads [ 0 ] != NULL )
{
for ( int i = 0; i < 4; i++ )
{
delete ( quads [ i ] );
}
} 
}

根据valgrind的说法,这是有效的,即没有内存泄漏和错误,但我不太确定是否是。你能谈谈你对析构函数有效性的看法吗?

第二个问题是,在这个类的重载赋值运算符的实现中,如果我在自赋值检查之后和这个方法中的任何其他操作之前写这个:

delete this;

我选

*"中的错误:双重释放或损坏(出):0x00007fffcb4d46d0*:第3行:4227中止(核心转储)泄漏检查

错误。

如果我写(析构函数中的相同代码)

if ( quads [ 0 ] != NULL )
{
for ( int i = 0; i < 4; i++ )
{
delete ( quads [ i ] );
}
}

相反,在重载赋值运算符中"delete this;",我没有得到任何错误。怎么了,你能澄清一下吗?

编辑:我删除了复制构造函数。

也许问题出在使用物体的自毁。以下是一些有用的信息:https://isocpp.org/wiki/faq/freestore-mgmt#delete-这个

在现代C++中,除非您处于非常特殊的情况,否则您应该使用智能指针类来管理拥有的指针,如std::unique_ptrstd::shared_ptr

请注意,STL智能指针可以很好地与STL容器相结合,从而可以实现一种"确定性(基于引用计数)垃圾收集器">

在你的情况下,我会考虑使用:

// Supports copy semantics
std::vector<std::shared_ptr<QTree>> quads;

或:

// This is movable but not copyable
std::vector<std::unique_ptr<QTree>> quads;

取决于您的需求和设计。

这样,您就可以享受所谓的"零规则">:C++编译器将自动为您实现复制构造函数、移动构造函数、复制赋值、移动赋值和析构函数。

C++中的
  1. delete特别允许0/nullptr作为参数,因此您的测试是肤浅的
  2. 将释放循环写入到实际矢量大小for ( int i = 0; i != quads.size(); i++ )更安全
  3. What is wrong, could you clarify me?。当您调用delete this时,您不仅调用了析构函数(带有quads释放),还调用了类的内存,实际上是将其取出。如果在堆栈上分配类,您将遇到更多问题。解决方案-重构解除分配并从任意位置调用

    private void QTree::deallocate() {
    for(int i = 0; i != quads.size(); ++i) {
    delete (quads[i]);
    }
    }
    QTree::~Qtree() {
    deallocate();
    ...
    }
    QTree& QTree::operator=(const QTree& rhs) {
    deallocate();
    ...
    }
    

如果我理解正确,你要做的是:

QTree& operator =(const QTree& rhs) {
// destroy current content
delete this;
// clone and copy content of rhs to current quads
if (rhs.quads[0] != NULL) {
for (int i = 0; i < 4; ++i) {
quads[i] = new QTree(*rhs.quads[i]);
}
}
return *this;
}

这样就可以避免在析构函数中重复代码

第一件事:为什么delete this没有像你预期的那样工作:那是因为在调用对象析构函数(这是你想要做的)之后,delete this也会释放对象的内存。您自己的当前对象,将用于指定从其他对象复制的值的对象。使用this->~Qtree()可以在不释放内存的情况下调用析构函数

事实上,也可以在不使用placement-new运算符分配内存的情况下调用构造函数,所以如果您已经有了一个复制构造函数,那么您的赋值运算符可以是:

QTree& operator=(const QTree& rhs) {
this->~QTree();
new(this)(rhs);
return *this;
}

看起来很整洁,但不幸的是,不建议这样做,因为这不是异常安全的:如果构造函数抛出异常,则您的对象将处于糟糕的状态。

实现赋值运算符的推荐方法之一是使用另一种"交换"方法或函数:

QTree& operator=(const QTree& rhs) {
QTree tmp(rhs);
swap(tmp)
return *this;
}

既然我已经回答了这个问题,那么您的代码还有一些其他问题,尤其是似乎没有必要使用可变长度向量来始终存储4个项目和4个项目。为什么不简单地使用QTree[4]数组?

最新更新