【问题标题】:Field variable deleted before actual destructor called?在调用实际析构函数之前删除了字段变量?
【发布时间】:2012-02-07 00:57:32
【问题描述】:

在删除对象后进入析构函数之前,什么会使字段变量过时? 我正在寻找这个问题的答案,我在这个网站上遇到了这个问题: Lifetime of object is over before destructor is called?

有些东西根本没有加起来:如果我在另一个 WrapperClass 中声明了一个指向 SomeClass 的指针,那么当我构造 WrapperClass 时,我需要创建一个 new SomeClassdelete 它包装的破坏。 这是有道理的,并且到目前为止一直有效。 指针在析构函数中仍然有效且正确,否则显然我无法删除它。

现在我的问题是,当我调用析构函数时,WrapperClass 的字段成员(一个 int 和一个指向 SomeClass 数组的指针)是垃圾。我在构造后检查了包装器对象,数据很好。包装器实际上是另一个Main 类中的指针,当我破坏Main(破坏包装器)时会出现问题,但如果我只是delete 来自Main 中的另一个方法的包装器,则工作正常。 我的偏执使我得到了上述答案,现在我完全糊涂了。 有人想了解一下这里到底发生了什么吗?

编辑: NodeSomeClass

class WrapperException{};
class Wrapper {
private:
    struct Node { /*....*/ };
    int numNodes;
    Node** nodes;
public:
    Wrapper() : numNodes(0) { nodes = new Node*[SIZE]; }
    Wrapper(const Wrapper& other) { throw WrapperException(); }
    Wrapper& operator=(const Wrapper& other) { throw WrapperException(); }
    ~Wrapper() { //calling delete Main gets me here with garbage for numNodes and nodes
        for(int i = 0; i < numNodes; i++)
            delete nodes[i];
        delete nodes;
    }
};

class MainException{};
class Main {
public:
    Main() { wrapper = new Wrapper(); }
    Main(const Main& other) { throw MainException(); }
    Main& operator=(const Main& other) { throw MainException(); }
    ~Main() { delete wrapper; }
private:
    Wrapper* wrapper;
};

【问题讨论】:

  • 你不是唯一一个困惑的人。向我们展示一些代码而不是描述它可能会有所帮助(请仅提供相关部分)。但只是一个想法:你没有打破三规则,是吗?
  • 如果你有一个非平凡的析构函数,你是否确保在所有构造函数中都初始化了资源?另外:请输入代码。
  • @Grizzly 我不是 3 规则的专家,但我认为它甚至不适用于这个非常简单的情况,不是吗?
  • 在调试器中运行,并在 Wrapper 析构函数中设置断点。然后你会看到它是否被调用了两次,这可能是问题之一。另一件事可能是您以某种方式使用了非法指针,例如超出列表或数组末尾的一步。
  • @JoachimPileborg 我在Main 的析构函数处设置了一个断点,该断点立即与垃圾一起进入Wrapper 的析构函数...

标签: c++ destructor


【解决方案1】:

既然灰熊没有回答,我就把它放在那里。

您的 Main 类和 Wrapper 类都需要正确实现的复制构造函数和赋值运算符。见The Rule of 3

问题是,如果你的类曾经被复制(这很容易在你没有意识到的情况下发生),那么指针就会被复制。现在你有两个对象指向同一个地方。当其中一个超出范围时,它的析构函数被调用,它在该指针上调用 delete,指向的对象被销毁。然后另一个对象留下一个悬空指针。当它被销毁时,它会尝试在该指针上再次调用 delete。

【讨论】:

    【解决方案2】:

    罢工>

    Node** nodes;
    

    应该是

    Node * nodes;
    

    析构函数也是错误的。应该是:

    for(int i = 0; i < numNodes; i++)
        delete nodes[i];
    delete [] nodes;
    

    可能还有其他问题,例如您还没有创建复制构造函数或赋值运算符,因此可能会使对象的副本为您删除该对象。

    编辑:更改了析构函数...

    【讨论】:

    • 但我想要指向节点的指针,而不是节点本身。编辑代码以在新的数组声明中反映这一点。
    【解决方案3】:

    您需要使用标准库来实现此行为。

    class Wrapper {
    private:
        struct Node { /*....*/ };
        int numNodes;
        std::vector<std::unique_ptr<Node>> nodes;
    public:
        Wrapper() : numNodes(0) { nodes.resize(SIZE); }
        // No explicit destructor required
        // Correct copy semantics also implemented automatically
    };
    
    class Main {
    public:
        Main() : wrapper(new Wrapper()) {}
        // Again, no explicit destructor required 
        // Copying banned for move-only class, so compiler tells you
        // if you try to copy it when you can't.
    private:
        std::unique_ptr<Wrapper> wrapper;
    };
    

    此代码保证正确执行。在 C++ 中,如果您使用过 new[]deletedelete[],请立即重构您的代码以删除它们,并检查三遍任何非放置 new 的使用 - 构造一个 unique_ptr 很漂亮这是唯一有效的案例。这不过是手动内存管理的常见预期结果。

    【讨论】:

    • 不幸的是,这是作业,所以 STL 是不行的。现在,如果我能理解破坏这种情况的副本分配发生在哪里,我就可以学会在下一次预见它。
    • @itchy23:如果你不能使用 STL,那么实现类似的东西然后使用它。如果不使用 C++ 的技术,您无法创建可靠的代码。这就是为什么它是标准的一部分。另外,如果你很绝望,那么只需声明一个抛出异常的复制构造函数——堆栈跟踪会告诉你副本的确切来源。
    • 抛出异常的好主意。我的目标不是为这个作业提供超级可靠的代码,但你是对的......
    • @itch23:为什么不声明复制构造函数/赋值操作(可能是私有的),而不定义它,所以编译器/链接器会告诉副本来自哪里。 DeadMG:您可能会提到您的代码仅是 C++11,因此对于许多(大多数?)未经修改就依赖于 boost 的程序员来说并不实用(这也不总是一种选择)。所以现在宣布unique_ptr 是唯一有效的情况可能为时过早(尽管我原则上同意)。
    • @Grizzly:在 MSVC 上,它不会告诉您链接器错误来自何处。那是我的第一个技术,现在我使用异常方法。虽然我的代码确实是 C++11,但问题标记为 C++,而不是 C++03。您在问题中陈述的就是卡在仅 C++03 的编译器或没有 Boost 依赖项。
    【解决方案4】:

    包装对象的生命周期已经结束,但整数和指针子对象以及指针对象仍然存在。当您在指针上调用delete 时,指针对象的生命周期结束,但指针仍然存在。指针的生命周期在您的 dtor 完成后结束。

    因此,如果您的成员已被破坏,那么还有其他事情正在发生。

    【讨论】:

    • 我的想法完全正确。这不是我第一次做这样的事情,所以我不知道我做了什么,这一次是如此不同。
    猜你喜欢
    • 2018-01-02
    • 1970-01-01
    • 2017-08-28
    • 2020-04-01
    • 2012-03-13
    • 2012-02-12
    • 2013-10-20
    • 2010-11-26
    • 1970-01-01
    相关资源
    最近更新 更多