【问题标题】:Does delete work with pointers to base class?删除是否适用于指向基类的指针?
【发布时间】:2010-09-22 15:15:36
【问题描述】:

您是否必须传递 delete 与 new 返回的相同指针,或者您可以传递一个指向类基类型之一的指针?例如:

class Base
{
public:
    virtual ~Base();
    ...
};

class IFoo
{
public:
    virtual ~IFoo() {}
    virtual void DoSomething() = 0;
};

class Bar : public Base, public IFoo
{
public:
    virtual ~Bar();
    void DoSomething();
    ...
};

Bar * pBar = new Bar;
IFoo * pFoo = pBar;
delete pFoo;

当然,这大大简化了。我真正想做的是创建一个充满 boost::shared_ptr 的容器,并将其传递给一些代码,该代码将在完成后将其从容器中删除。此代码对 Bar 或 Base 的实现一无所知,并将依赖 shared_ptr 析构函数中隐含的删除运算符来做正确的事情。

这可能行得通吗?我的直觉说不,因为指针不会有相同的地址。另一方面, dynamic_cast 应该可以工作,因此编译器在某个地方存储了足够的信息来解决这个问题。


感谢您的帮助,所有回答和评论的人。我已经知道虚拟析构函数的重要性,如我的示例所示;看到答案后,我稍微思考了一下,并意识到虚拟析构函数的全部原因就是这种情况。因此它必须工作。我被缺少将指针转换回原始指针的可见方法而感到震惊。更多的思考让我相信有一种看不见的方法,我推测析构函数正在返回真正的删除指针以释放。当我在 ~Base 中看到这一行时,调查来自 Microsoft VC++ 的编译代码证实了我的怀疑:
mov eax, DWORD PTR _this$[ebp]

跟踪汇编程序发现这是传递给删除函数的指针。谜团解开了。

我已经修复了将虚拟析构函数添加到 IFoo 的示例,这是一个简单的疏忽。再次感谢所有指出这一点的人。

【问题讨论】:

    标签: c++ boost polymorphism


    【解决方案1】:

    是的,它会起作用,当且仅当基类析构函数是虚拟的,您已经为 Base 基类而不是 IFoo 基类做到了这一点。如果基类析构函数是虚拟的,那么当你在基类指针上调用operator delete时,它会使用动态调度,通过在虚函数表中查找派生类析构函数来确定如何删除对象。

    在您的多重继承的情况下,只有当您删除它的基类具有虚拟析构函数时,它才会起作用;其他基类可以没有虚拟析构函数,但前提是您不尝试通过其他基类指针删除任何派生对象。

    【讨论】:

    • 即使没有虚拟析构函数,实际内存也可能会被释放。
    • 问题是你没有在你需要的地方包含一个虚拟析构函数。您在 Base 中包含了一个虚拟析构函数,但在 IFoo 中没有。您的 delete 语句调用 IFoo 中的非虚拟析构函数,因此它不会过滤到 Bar,不会过滤回 Base,并且会泄漏内存。
    • 恐怕 JaredPar 和 Evan 没有仔细看这个例子。
    • 具体来说:Bar 的析构函数“神奇地”被虚拟化(除非它不需要,因为它被声明为虚拟的),但 IFoo 的析构函数不是,这就是被调用的析构函数在问题的代码中。
    • 如果你想在这个行业生存下去,你最好不要看实际的代码,而要根据想象的代码给出答案。
    【解决方案2】:

    这与您给定的示例无关,但由于您提到您在删除其拥有的对象时对shared_ptr 的行为非常感兴趣,您可能对使用shared_ptr 的“删除器”感兴趣.

    如果shared_ptr 拥有的对象在被删除时需要特殊处理,您可以为任何特定的shared_ptr<> 指定“删除器”。删除器不是类型的一部分,它是 shared_ptr<> 实例的属性,因此您的 shared_ptr<> 对象容器可能包含一些具有不同删除器的对象。以下是 Boost 文档对 shared_ptr<> 删除器的评价:

    自定义释放器允许工厂 函数返回一个shared_ptr 将用户与其记忆隔离 分配策略。由于 释放器不是类型的一部分, 改变分配策略确实 不破坏源代码或二进制文件 兼容性,并且不需要一个 客户端重新编译。例如,一个 “no-op”释放器在以下情况下很有用 将shared_ptr 静态返回给 分配的对象和其他变体 允许将 shared_ptr 用作 另一个智能指针的包装器, 简化互操作性。

    如果您可以将IFoo 修改为具有虚拟析构函数,那将是最干净的,因为您计划通过IFoo 引用或指针删除属于它的子类的对象。但是,如果您遇到无法更正的IFoo,那么如果您想在容器中使用shared_ptr<IFoo>,但让它指向Bar,您可以使用删除器创建shared_ptr 实例执行向下转换到 Bar* 然后执行删除操作。 Downcasts 被认为是不好的形式,但这种技术可能是您可以在绑定中使用的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-05
      • 2015-07-02
      • 2011-09-14
      • 2013-02-11
      • 2010-09-08
      • 1970-01-01
      • 2011-05-03
      • 2020-03-27
      相关资源
      最近更新 更多