Delete[]没有调用元素的析构函数。

3

我已经开始使用trie实现一个字典。基本上,我有一个根节点,它有一个定义(指向代表与键关联的值的T指针)和子节点(指向包含256个节点指针的数组,每个字母一个节点)。请查看以下定义:

template<typename T>
class DiccString {
        public:
                DiccString() : root(NULL);
                DiccString(const DiccString<T>&);
                ~DiccString() {delete root;}

                void define(const string& key, const T& value);
                bool isDefined(const string& key) const;
                const T& getDefinition(const string& key) const;
                T& getDefinition(const string& key);
                void remove(const string& key);
                const Set<string>& keys() const;

        private:

                struct Node{
                    Node** childs;
                    T* definition;
                    Node(){
                        std::cout << "Node has been created " << this << std::endl;
                        childs = new Node*[256];
                        definition = NULL;
                    }
                    ~Node(){
                        std::cout << "Node has been deleted " << this << std::endl;
                        delete definition;
                        delete [] childs;
                    }
                };

                Node* root;
};

所以,假设没有其他键,如果我想要存储值为14的"John"(T将会是int),那么我需要创建一个根节点,然后在root->childs[(int)'j']上创建另一个节点“nodeJ”,然后是nodeJ->childs[(int)'o'],一直到达最后一个节点“nodeN”,该节点将包含值(nodeN->definition = 14)。
问题出在当我执行以下操作时:
int main() {
    DiccString<int> d;
    d.define("john",20);
    d.define("jane",25);

    return 0;
}

我希望所有创建的节点都能被销毁,但请看输出结果:

Node created 0x61fc20 // root
Node created 0x620860 // for letter 'j'
Node created 0x621090 // for letter 'o' (child of 'j' 0x620860)
Node created 0x6218c0 // for letter 'h' (child of 'o' 0x621090)
Node created 0x6220f0 // for letter 'n' (child of 'h' 0x6218c0), value: 20
Node created 0x622990 // for letter 'a' (child of 'j' 0x620860)
Node created 0x6231c0 // for letter 'n' (child of 'a' 0x622990)
Node created 0x6239f0 // for letter 'e' (child of 'n' 0x6231c0), value: 25
Node deleted 0x61fc20 // root

只删除了根。所以显然,在Node的析构函数中执行delete [] childs时,它没有删除数组的所有元素,我确定这些元素实际上是存在的:例如,在调用根的析构函数时(实际上只有这个被调用),我评估了childs[(int)'j'],它肯定是0x620860,所以当执行delete [] childs时,它应该调用这个元素(至少)的析构函数,对吗?
我做错了什么?
2个回答

2

childs是一个指向指针的指针,类型为Node**。你需要为它分配一个Node*指针数组。

delete[] childs会删除这个内存分配,只会释放指针所占用的空间,而不会释放指针所指向的对象。

我们无法看到你实际分配Node的代码,但你必须存储哪些256个Node*指针实际上指向有效的Node对象。也许你通过使用NULL指针来标记指针,以此来完成。如果是这样的话,你可能会想要做以下操作:

for(int i=0; i<256; ++i) {
    delete childs[i];
}

需要注意的是,如果节点指针的数量是固定的,则应使用静态数组,否则应使用std::vector


哦,我明白了。我明天会尝试解决它。谢谢你的回答! - jscherman

0

打印仅在销毁Node对象时发生,而不是Node *。您可以通过使用调试器来检查行为是否正确。

请注意,与其在define()中懒惰分配,如果构造函数/析构函数之间具有对称性,则可能更清晰。还要确保在解除分配时正确处理DiccString为空的情况。


构造函数和析构函数之间的对称性是什么意思?(顺便感谢您的回答) - jscherman
1
DiccString() 应该有分配内存的操作,而 ~DiccString() 应该有相应的释放内存的操作。 - Brian Cain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接