假设我们有一个BST_Node类:
struct BST_Node {
BST_Node* left;
BST_Node* right;
}
还有一个类AVL_Node:
struct AVL_Node : BST_Node {
int height;
}
在某些功能上
void destroyTree() {
BST_Node *mynode = new AVL_Node;
delete mynode; // Is it ok ?
}
当析构函数是非虚拟的,但是派生类中只有基本类型时,在基类上调用delete是安全的吗?(不会有内存泄漏吗?)
仅在派生类中声明析构函数虚拟时的规则是什么?据我了解,所有析构函数都是同一个函数,我们可以将其称为destructor(),然后在删除基指针时,仅针对基类调用析构函数,但是在删除派生类时,析构函数也会被分派到子派生类中。
当析构函数是非虚拟的,但是派生类中只有基本类型时,在基类上调用delete是安全的吗?(不会有内存泄漏吗?)
您可能没有意识到,但这是两个不同的问题。
后一个答案是:不,对于此特定示例,不会有任何内存泄漏,但是对于其他示例,可能会发生。
答案是前一个问题的原因:不,这样做是不安全的。即使几乎所有编译器对此行为都已很好地理解,这也构成了未定义的行为-而且,为了清楚起见,“理解”并不是“安全地做”的同义词。
当您编写类似delete mynode;
的代码时,编译器必须弄清楚要调用哪个析构函数。如果的析构函数mynode
不是虚拟的,则它将始终使用基本析构函数,执行基本析构函数需要执行的任何操作,而不执行派生的析构函数需要执行的任何操作。
在这种情况下,这没什么大不了的:唯一要AVL_Node
添加的是本地分配的int
变量,该变量将作为清理整个指针的同一过程的一部分进行清理。
但是,如果您的代码是这样的:
struct AVL_Node : public BST_Node {
std::unique_ptr<int> height = std::make_unique<int>();
};
然后,即使我们在派生对象的构造中明确使用了智能指针,此代码也肯定会导致内存泄漏!智能指针不会使我们免于将delete
基指针与非virtual
析构函数结合在一起的麻烦。
通常,如果代码AVL_Node
负责其他对象,则代码可能导致任何类型的泄漏,包括但不限于资源泄漏,文件句柄泄漏等。例如,考虑一下是否AVL_Node
有这样的情况,这在某些类型的图形代码中极为常见:
struct AVL_Node : public BST_Node {
int handle;
AVL_Node() {
glGenArrays(1, &handle);
}
/*
* Pretend we implemented the copy/move constructors/assignment operators as needed
*/
~AVLNode() {
glDeleteArrays(1, &handle);
}
};
您的代码不会泄漏内存(在您自己的代码中),但是会泄漏OpenGL对象(以及该对象分配的所有内存)。
仅在派生类中声明析构函数虚拟时的规则是什么?
如果您从不打算存储指向基类的指针,那很好。
除非您还计划创建派生类的其他派生实例,否则也没有必要。
因此,以下是为清楚起见而使用的示例:
struct A {
std::unique_ptr<int> int_ptr = std::make_unique<int>();
};
struct B : A {
std::unique_ptr<int> int_ptr_2 = std::make_unique<int>();
virtual ~B() = default;
};
struct C : B {
std::unique_ptr<int> int_ptr_3 = std::make_unique<int>();
//virtual ~C() = default; // Unnecessary; implied by B having a virtual destructor
};
现在,这里是可以与这三个类一起使用的所有安全代码和不安全代码:
auto a1 = std::make_unique<A>(); //Safe; a1 knows its own type
std::unique_ptr<A> a2 = std::make_unique<A>(); //Safe; exactly the same as a1
auto b1 = std::make_unique<B>(); //Safe; b1 knows its own type
std::unique_ptr<B> b2 = std::make_unique<B>(); //Safe; exactly the same as b1
std::unique_ptr<A> b3 = std::make_unique<B>(); //UNSAFE: A does not have a virtual destructor!
auto c1 = std::make_unique<C>(); //Safe; c1 knows its own type
std::unique_ptr<C> c2 = std::make_unique<C>(); //Safe; exactly the same as c1
std::unique_ptr<B> c3 = std::make_unique<C>(); //Safe; B has a virtual destructor
std::unique_ptr<A> c4 = std::make_unique<C>(); //UNSAFE: A does not have a virtual destructor!
因此,如果B
(具有virtual
析构函数的类)是从A
(无virtual
析构函数的类)继承的,但是作为程序员,您保证永远不会B
使用A
指针引用的实例,那么您无需担心。因此,在这种情况下,就像我的示例试图说明的那样,可能有充分的理由声明派生类的析构函数,virtual
而使超类的析构函数为非virtual
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句