【问题标题】:Destructor seems to be called 'early'析构函数似乎被称为“早期”
【发布时间】:2011-05-31 19:14:56
【问题描述】:

原来这是一个简单的构造函数误用问题。 有关更新信息,请参阅“编辑”部分。

抱歉,还有一个 C++ dtor 问题... 但是我似乎找不到一个与我的完全一样的,因为所有其他容器都分配给 STL 容器(这将删除对象本身),而我的是一个指针数组。

所以我有以下代码片段

#include<iostream>

class Block{
public:
    int x, y, z;
    int type;
    Block(){
        x=1;
        y=2;
        z=3;
        type=-1;
    }
};

template <class T> class Octree{
    T* children[8];
public:
    ~Octree(){
        for( int i=0; i<8; i++){
            std::cout << "del:" << i << std::endl;
            delete children[i];
        }
    }    
    Octree(){
        for( int i=0; i<8; i++ )
            children[i] = new T;
    }
    // place newchild in array at [i]
    void set_child(int i, T* newchild){
        children[i] = newchild;
    }
    // return child at [i]
    T* get_child(int i){
        return children[i];
    }
    // place newchild at [i] and return the old [i]
    T* swap_child(int i, T* newchild){
        T* p = children[i];
        children[i] = newchild;
        return p;
    }
};

int main(){
    Octree< Octree<Block> > here;
    std::cout << "nothing seems to have broken" << std::endl;
}

查看输出,我注意到析构函数在我认为应该调用之前被调用了很多次(因为 Octree 仍在范围内),输出的结尾还显示:

del:0
del:0
del:1
del:2
del:3

Process returned -1073741819 (0xC0000005)   execution time : 1.685 s
Press any key to continue.

由于某种原因,析构函数在循环中经过同一点两次 (0),然后死亡。

所有这一切都发生在“似乎没有任何问题”行之前,这是我在调用任何 dtor 之前所期望的。

提前致谢:)

编辑 我发布的代码删除了一些我认为不必要的东西,但是在复制和编译我粘贴的代码后,我不再收到错误。 我删除的是代码的其他整数属性。 以下是原文:

#include<iostream>

class Block{
public:
    int x, y, z;
    int type;
    Block(){
        x=1;
        y=2;
        z=3;
        type=-1;
    }
    Block(int xx, int yy, int zz, int ty){
        x=xx;
        y=yy;
        z=zz;
        type=ty;
    }
    Block(int xx, int yy, int zz){
        x=xx;
        y=yy;
        z=zz;
        type=0;
    }
};

template <class T> class Octree{
    int x, y, z;
    int size;
    T* children[8];
public:
    ~Octree(){
        for( int i=0; i<8; i++){
            std::cout << "del:" << i << std::endl;
            delete children[i];
        }
    }

    Octree(int xx, int yy, int zz, int size){
        x=xx;
        y=yy;
        z=zz;
        size=size;
        for( int i=0; i<8; i++ )
            children[i] = new T;
    }
    Octree(){
        Octree(0, 0, 0, 10);
    }
    // place newchild in array at [i]
    void set_child(int i, T* newchild){
        children[i] = newchild;
    }
    // return child at [i]
    T* get_child(int i){
        return children[i];
    }
    // place newchild at [i] and return the old [i]
    T* swap_child(int i, T* newchild){
        T* p = children[i];
        children[i] = newchild;
        return p;
    }
};

int main(){
    Octree< Octree<Block> > here;
    std::cout << "nothing seems to have broken" << std::endl;
}

此外,对于 set_child、get_child 和 swap_child 可能导致内存泄漏的问题,这将得到解决,因为包装类将在 set 之前使用 get 或使用 swap 来获取旧子节点并在释放之前将其写入磁盘记忆本身。

我很高兴这不是我的内存管理失败,而是另一个错误。 我还没有制作副本和/或赋值运算符,因为我只是在测试块树,我几乎肯定会很快将它们全部设为私有。

这个版本吐出-1073741819。

感谢大家的建议,对于劫持我自己的帖子我深表歉意:$

已解决 一个构造函数调用另一个构造函数的问题。

感谢大家的帮助,对浪费的时间表示歉意:)

【问题讨论】:

  • 除其他外,您还有一些严重的内存泄漏;例如:children[i] = newchild;。您是否考虑过使用拥有资源的智能指针,例如 auto_ptrshared_ptr
  • 很容易计算,在销毁Octree&lt; Octree&lt;Block&gt; &gt; here;del= ...时会打印64次。现在,问题是什么?
  • 为我工作。实际问题是什么?注意(我预计 del 会被打印 72 次,我得到 72 (8*8 + 8) 快速浏览显示它们似乎是正确的顺序)。
  • 抱歉,我删除了 Block 和 Octree 的一些其他整数属性,错误似乎源于它们,我的完整代码现在在我的原始帖子中的“编辑”之后,对此非常抱歉。

标签: c++ memory dynamic destructor


【解决方案1】:

有人定义了构造函数和析构函数,但没有复制构造函数。是被销毁的副本弄乱了计数。关注rule of three

【讨论】:

  • 请注意,我实际上并没有看到任何副本在这里发生,但不遵守三规则肯定会很麻烦。
  • 这并不是我认为这是 bad 建议,但我只是不认为 OP 的问题是由此引起的,因为他没有调用任何函数除了构造函数和析构函数。
【解决方案2】:

问题出在默认构造函数(在编辑之前您没有添加!);它构造了一个临时的 Octree 实例,我希望你认为它会简单地调用另一个构造函数:

Octree(){
    Octree(0, 0, 0, 10);
}

您看到的正是这个实例在崩溃前被破坏。然后,您尝试删除一些从未newed(或初始化)的children

Octree(int, int, int, int ) 中提取初始化代码到方法中将解决您的问题。例如:

Octree(int xx, int yy, int zz, int size){
    init(xx, yy, zz, size);
}
Octree(){
    init(0, 0, 0, 10);
}

void init(int xx, int yy, int zz, int)
{
    x=xx;
    y=yy;
    z=zz;
    for( int i=0; i<8; i++ )
        children[i] = new T;

}

或者,删除默认构造函数并将默认值添加到剩余构造函数的每个参数:

Octree(int xx = 0, int yy = 0, int zz = 0, int size = 10)
    :x(xx)
    ,y(yy)
    ,z(zz)
    ,size(size)
{
    for( int i=0; i<8; i++ )
        children[i] = new T;
}

但是,您真的真的需要处理原始指针吗?如果你这样做了,那么你几乎肯定需要在你的课程有用之前做something about copying

回复your next question, "Nope!""not until C++11!"

(及其继任者):

您现在可以委派构造,但语法与您尝试表达的方式略有不同:

Octree()
    :Octree(0, 0, 0, 10)
{
}

【讨论】:

  • 感谢 Johnsyweb,这正是我的问题,我很尴尬 >.> 所有这些都在使用解释语言和/或 java。
【解决方案3】:

它不会两次经历相同的循环。您的顶级八叉树有 8 个子八叉树,因此您看到的是嵌套破坏。我不确定它为什么会死。

【讨论】:

    【解决方案4】:

    我怀疑您只是完成了破坏并且程序在所有输出写入控制台之前终止了。预计事物会通过 0 两次,因为它是 Octree&lt;&gt; 0 和 Octree&lt;Octree&lt;&gt;&gt; 0。

    您需要更改代码以保证析构函数运行,并且所有控制台 I/O 在进程退出之前完成。

    int main(){
     {
      Octree< Octree<Block> > here;
     }
     std::cout << "nothing seems to have broken" << std::endl;
     std::cin.get();
    }
    

    当然,这段代码还有很多其他缺陷。但我将从提出的确切问题开始。

    【讨论】:

    • 以负返回码退出的进程是一个指标,在我知道的每个平台上的正常进程退出时,缓冲区都会刷新到控制台。只要没有未定义的行为发生,析构函数就可以保证运行。
    • @Billy:该代码还在我的 P&C 上产生了预期的结果——一点也不可疑,而且我在他实际调用的函数中看不到任何问题。我其实没有其他想法。我并不是要暗示析构函数没有运行,而是他没有给自己检查输出的能力。 0xC0000005 是 Windows 上的 AV,但我不明白他是如何造成的。
    • 我更新后的代码在控制台产生了意外的输出,因为双 0 接近结尾,而在工作(原始代码)中它位于开头。此外,我更新的代码中根本没有打印“似乎没有任何问题”。我的原始帖子的代码产生了我期望看到的结果。
    【解决方案5】:

    valgrind 说:

    ==11907== HEAP SUMMARY:
    ==11907==     in use at exit: 0 bytes in 0 blocks
    ==11907==   total heap usage: 72 allocs, 72 frees, 1,536 bytes allocated
    ==11907== 
    ==11907== All heap blocks were freed -- no leaks are possible
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多