Delete[] 没有调用元素析构函数
Delete[] is not calling elements destructors
我已经开始使用 trie 实现字典。基本上,我有一个根节点,它有一个定义(指向 T
的指针,表示与键关联的 de 值)和子节点(指向包含指向 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
所以),假设没有其他键,那么我会创建一个根,然后在根 ->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
时,它并没有删除数组的所有元素,我确信它们存在:例如,在调用的情况下root 的析构函数(这是唯一实际被调用的析构函数),我评估了 childs[(int)'j']
并且它肯定是 0x620860,所以我知道它应该在执行时调用这个元素的析构函数(至少)delete [] childs
,对吧?
我做错了什么?
打印输出只发生在 Node
个对象的销毁时,而不是 Node *
。您可以使用调试器检查行为是否正确。
请注意,不要在 define()
中延迟分配,如果 constructor/destructor 之间具有对称性,可能会更清楚。还要确保正确处理释放时 DiccString
为空的情况。
childs
的类型是 Node**
,它是一个指向指针的指针。你为它分配了一个 Node*
指针数组。
delete[] childs
删除此分配,即只删除指针的内存,而不是指针指向的对象。
我们看不到您实际分配 Node
的代码,但您必须以某种方式存储某处分配的 256 个 Node*
中的哪一个实际指向有效的 Node
对象.也许你通过用 NULL 指针标记指针来做到这一点?在那种情况下,您可能想做类似的事情:
for(int i=0; i<256; ++i) {
delete childs[i];
}
我还应该注意,如果节点指针的数量是固定的,你应该使用静态数组,否则 std::vector
。
我已经开始使用 trie 实现字典。基本上,我有一个根节点,它有一个定义(指向 T
的指针,表示与键关联的 de 值)和子节点(指向包含指向 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
所以),假设没有其他键,那么我会创建一个根,然后在根 ->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
时,它并没有删除数组的所有元素,我确信它们存在:例如,在调用的情况下root 的析构函数(这是唯一实际被调用的析构函数),我评估了 childs[(int)'j']
并且它肯定是 0x620860,所以我知道它应该在执行时调用这个元素的析构函数(至少)delete [] childs
,对吧?
我做错了什么?
打印输出只发生在 Node
个对象的销毁时,而不是 Node *
。您可以使用调试器检查行为是否正确。
请注意,不要在 define()
中延迟分配,如果 constructor/destructor 之间具有对称性,可能会更清楚。还要确保正确处理释放时 DiccString
为空的情况。
childs
的类型是 Node**
,它是一个指向指针的指针。你为它分配了一个 Node*
指针数组。
delete[] childs
删除此分配,即只删除指针的内存,而不是指针指向的对象。
我们看不到您实际分配 Node
的代码,但您必须以某种方式存储某处分配的 256 个 Node*
中的哪一个实际指向有效的 Node
对象.也许你通过用 NULL 指针标记指针来做到这一点?在那种情况下,您可能想做类似的事情:
for(int i=0; i<256; ++i) {
delete childs[i];
}
我还应该注意,如果节点指针的数量是固定的,你应该使用静态数组,否则 std::vector
。