在c ++

时间:2015-04-30 16:24:46

标签: c++ pointers memory-management quadtree

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

我正在实现一个区域四叉树结构。在这个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 ] );
        }
    }

而不是“删除此”;在重载的as​​sigment运算符中,我没有错误。 有什么不对,你能澄清一下吗?

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

也许问题是使用对象的自毁。以下是一些有用的信息:https://isocpp.org/wiki/faq/freestore-mgmt#delete-this

3 个答案:

答案 0 :(得分:4)

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

请注意,STL智能指针可以很好地与STL容器结合使用,从而可以实现一种&#34;确定性(基于引用计数)的垃圾收集器&#34; 。 / p>

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

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

或:

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

取决于您的需求和设计。

通过这种方式,您可以享受所谓的gulp-angular-templatecache:C ++编译器将自动实现复制构造函数,移动构造函数,复制赋值,移动赋值和析构函数。

答案 1 :(得分:1)

    delete在C ++中特别允许0 / nullptr作为参数,因此您的测试是肤浅的
  1. 更安全地将释放循环写入实际矢量大小for ( int i = 0; i != quads.size(); i++ )
  2. What is wrong, could you clarify me?。当你调用delete this时,你不仅要调用析构函数(使用四元组释放),你就可以解除你的类的记忆,直接将它取出来。如果在堆栈上分配了类,则会遇到更多问题。解决方案 - 重构解除分配并从任何地方调用

    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();
        ...
    }
    

答案 2 :(得分:1)

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

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;
}

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

实现赋值运算符的一种推荐方法是使用nothrow“swap”方法或函数:

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

现在我回答了这个问题,你的代码还有一些其他问题,特别是使用可变长度向量来总是存储4个项目并且总是存储4个项目似乎没有必要。为什么不只是一个QTree [4]数组?