51

您是否必须传递 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<Bar*> 应该可以工作,因此编译器在某个地方存储了足够的信息来解决这个问题。


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

mov eax, DWORD PTR _this$[ebp]

跟踪汇编器发现这是传递给删除函数的指针。谜团已揭开。

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

4

2 回答 2

64

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

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

于 2008-11-17T05:19:37.790 回答
3

这与您给定的示例无关,但是由于您提到您在shared_ptr删除其拥有的对象时对 ' 的行为非常感兴趣,因此您可能对使用shared_ptr'deleter' 感兴趣。

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

自定义释放器允许工厂函数返回 ashared_ptr以将用户与其内存分配策略隔离开来。由于释放器不是类型的一部分,因此更改分配策略不会破坏源或二进制兼容性,并且不需要客户端重新编译。例如,“无操作”释放器在将 a 返回shared_ptr到静态分配的对象时很有用,而其他变体允许将 ashared_ptr用作另一个智能指针的包装器,从而简化互操作性。

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

于 2008-11-17T07:19:09.453 回答