派生类与基类的类型兼容性会导致内存泄漏吗?

发布于 2024-10-10 07:00:44 字数 1045 浏览 5 评论 0原文

我知道派生类与其基类的指针类型兼容。在给定的示例代码中,new bar 对象构造是通过调用 foo::foo() 进行的,然后调用 bar::bar()。在各自的构造函数中,我将资源分配给类成员 foo::int *abar::int *b

现在我将由此构造的对象初始化为基类类型。使用 obj,我可以调用基类析构函数,但不能调用派生类析构函数。那么,在这种情况下如何释放派生类资源呢?这不是内存泄漏吗?

#include <iostream>
class foo
{
    int *a;
public:
    foo()
    {
        a = new int[5];
        std::cout << "\n foo constructor" << std::endl;
    }
    ~foo()
    {
        std::cout << "\n foo destructor" << std::endl;
        delete[] a;
    }
};

class bar : public foo
{
    int *b;
public:
    bar()
    {
        b = new int[5];
        std::cout << "\n bar constructor" << std::endl;
    }
    ~bar()
    {
        std::cout << "\n bar destructor" << std::endl;
        delete[] b;
    }
};

int main()
{
    foo *obj = new bar; // Derived class object is type compatible with base class

    delete obj; // Equivalent to obj->~foo();
    return 0;
}

谢谢。

I understand that derived class is type compatible with a pointer to its base class. In the given sample code, new bar object construction takes place calling foo::foo() followed by bar::bar(). In the respective constructors, I am allocating resources to class members foo::int *a and bar::int *b.

Now I am initializing thus constructed object to base class type. With obj, I can call the base class destructor but not the derived class destructor. So, how can I deallocate the derived class resources in this case? Is this not a memory leak ?

#include <iostream>
class foo
{
    int *a;
public:
    foo()
    {
        a = new int[5];
        std::cout << "\n foo constructor" << std::endl;
    }
    ~foo()
    {
        std::cout << "\n foo destructor" << std::endl;
        delete[] a;
    }
};

class bar : public foo
{
    int *b;
public:
    bar()
    {
        b = new int[5];
        std::cout << "\n bar constructor" << std::endl;
    }
    ~bar()
    {
        std::cout << "\n bar destructor" << std::endl;
        delete[] b;
    }
};

int main()
{
    foo *obj = new bar; // Derived class object is type compatible with base class

    delete obj; // Equivalent to obj->~foo();
    return 0;
}

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

怀中猫帐中妖 2024-10-17 07:00:44

这就是“虚拟析构函数”的想法出现的地方。从技术上讲,如果通过基类类型的指针删除一个对象,则必须将该基类析构函数标记为虚拟,否则结果是未定义的。如果你确实将析构函数标记为virtual,则其含义与其他虚函数不同。它的意思不是“派生类重写此行为”,而是“通过基类指针删除此对象时,在调用基类构造函数之前调用派生析构函数”。这就是您想要的行为。

作为一般规则,您定义的任何计划子类化的类都应该有一个虚拟析构函数来防止此类问题。

This is where the idea of the "virtual destructor" comes in. Technically speaking, if you delete an object through a pointer of a base class type, you must mark that base class destructor virtual or the result is undefined. If you do mark the destructor virtual, the meaning is different from other virtual functions. Instead of meaning "derived classes override this behavior," it means "when deleting this object through a base class pointer, call the derived destructors before calling the base constructor." This is the behavior you want.

As a general rule, any class you define that you plan on subclassing should have a virtual destructor to prevent this sort of problem.

说好的呢 2024-10-17 07:00:44

如果您通过指向其基类之一的指针删除派生类对象,则基类析构函数必须声明为虚拟,否则行为未定义。

如果 ~foo() 被声明为 virtual,那么就可以开始了。首先调用 ~bar(),然后调用 ~foo()

If you delete a derived class object via a pointer to one of its base classes, the base class destructor must be declared virtual, otherwise the behavior is undefined.

If ~foo() is declared virtual, you're good to go. ~bar() will be called first, then ~foo().

寄意 2024-10-17 07:00:44

这实际上是一种未定义的行为。

来自标准文档。 5.3.5.3 删除

在第一个替代方案(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型
应是操作数动态类型的基类,静态类型应具有虚拟析构函数,或者行为是
未定义。 ......

It is actually an undefined behavior.

From Standard docs. 5.3.5.3 Delete,

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type
shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is
undefined.
......

无力看清 2024-10-17 07:00:44

执行此操作,

virtual ~foo()
{
   //your code
}

这可确保执行 delete *pFoo 也会调用派生类析构函数 (~bar())。调用顺序为~bar(),后跟~foo()


如果您对 ~bar() 也做同样的事情也会很好,也就是说,

virtual ~bar()
{
   //your code
}

尽管如果您不想进一步从 bar 派生,那么对于这种情况来说这并不是那么必要 并希望将 bar* 用于它的派生类。

Do this,

virtual ~foo()
{
   //your code
}

This ensures that doing delete *pFoo also invokes derived classes destructor (~bar()). The order of invocation would be ~bar() followed by ~foo().


It will also be good if you do the same for ~bar() also, that is,

virtual ~bar()
{
   //your code
}

Although it's not that much necessary for this scenario if you don't want to further derive from bar and want to use bar* for it's derived classes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文