使析构函数不是虚拟的并在特殊情况下删除基本指针是否安全?

拉富

假设我们有一个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 ?
}

问题1

当析构函数是非虚拟的,但是派生类中只有基本类型时,在基类上调用delete是安全的吗?(不会有内存泄漏吗?)

问题2

在派生类中声明析构函数虚拟时的规则是什么据我了解,所有析构函数都是同一个函数,我们可以将其称为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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

特殊情况下的智能指针析构函数

在不使用虚拟析构函数的情况下删除继承的对象时中止

在没有复制删除的情况下,是否可以确保在析构函数之前调用复制/移动构造函数?

在特殊情况下使用void **是否安全?还是不确定的行为?

在C ++中,是否有一种(新的)方法可以在不实例化实例的情况下测试类在模板中是否具有虚拟析构函数?

如何在不破坏move和copy构造函数的情况下声明虚拟析构函数

有什么方法可以检测是否使用非虚拟基本析构函数正确删除了类?

在特殊情况下:&比%快吗?

在以下情况下,手动调用析构函数是否是一个错误的设计决策?

是否可以在不调用 std async 的情况下使用阻塞析构函数创建 std 未来?

删除析构函数中的指针

从基本析构函数对指向指针的类的static_cast的安全性

通过指向基的指针删除对象,而没有虚拟析构函数

我是否需要显式调用基本虚拟析构函数?

在派生类中声明非虚拟析构函数是否安全?

在某些特殊情况下需要从删除中保存一行

在这种特殊情况下,静态数组的速度慢700倍是否合理?

在特殊情况下,是否有任何命令读取其标准输入?

在这种特殊情况下,字符串size()函数与strlen之间的区别

非指针成员变量的虚拟析构函数

如何在不调用析构函数的情况下将值移出 std:optional ?

在不调用析构函数、复制或移动的情况下初始化(分配)内存

基本接口类中缺少虚拟析构函数?

GHCi中特殊情况下的功能定义

特殊情况下的字符串压缩

仅在特殊情况下异步填充变量

spss IF循环MISSINGS在特殊情况下被忽略

XOR功能在特殊情况下失败

特殊情况下的买家怎么统计