【问题标题】:Why doesn't shared_ptr have a virtual descructor? (and how can I get around this?)为什么 shared_ptr 没有虚拟析构函数? (我怎样才能解决这个问题?)
【发布时间】:2011-04-17 15:06:53
【问题描述】:

我想制作一个特殊版本的shared_ptr,它可以在创建或销毁它时执行特定操作,但我的计划似乎因意识到shared_ptr 的析构函数是非虚拟的而受挫,这意味着当我覆盖它,当它们的最后一个实例被销毁时,我的指针永远不会被清理。

想到的唯一替代方法是将这种行为构建到我想与我假设的自定义 shared_ptr 一起使用的每个类中,这是不可行的(或在某些情况下是可能的)。

编辑:

我想要这个的原因是因为我想在 lua 中使用一些类作为 userdata 对象,并且我希望我使用这种方式的每个对象都有一个唯一的 fenv 表,当所有的对对象的引用已被删除。我计划使用指针的地址,因为它们键入包含 fenv 表的表。

假设我有一个可以将其他小部件作为子小部件的小部件。我在 Lua 中创建了两个小部件,然后将一个设置为另一个的子小部件,并删除对子小部件的所有 lua 引用(它是一个子小部件的事实在 C++ 中处理)。 GC 现在可以随时运行并删除子代。我不一定想让孩子运行它的析构函数,所以我想让它成为一个 shared_ptr。这样,在 Lua 清理之后,C++ 对象仍然可以使用它。如果我已经为其 fenv 分配了值或功能,我仍然希望能够访问它们。只有在删除对我的子小部件的最终引用时,我才希望完全删除 fenv 表。

【问题讨论】:

  • 您能告诉我们您打算在shared_ptr 的专业化方面做什么吗?可能有其他方法..
  • 像往常一样,告诉我们目标而不是步骤。它没有虚拟析构函数,因为它不是基类。
  • 更新了问题,说明我想这样做的原因。
  • 如果你想让shared_ptr 有一个虚拟析构函数,那么你对shared_ptr 的用途有一个根本的误解。 shared_ptr 的全部意义在于它充当值类型。听起来您只需要提供一个自定义删除器,为什么这在您的情况下不起作用?
  • 因为我不知道自定义删除器:)

标签: c++ shared-ptr virtual-destructor


【解决方案1】:

它已经内置了这种能力,不需要让人们做危险的事情,比如从中派生出来:

#include <boost/shared_ptr.hpp>
#include <iostream>

/*
 * Done as a function for simplicity.
 * But this can be done in so many ways
 */
void MyCleanup(int* x)
{
    std::cout << "DONE\n";
    delete x;
}

int main()
{
    boost::shared_ptr<int>  x(new int(5), MyCleanup);

}

导出问题:
就在我的脑海中。

class X: public shared_ptr<int> { /* STUFF. With a special destructor. */ };

int main()
{
    /* what happens now? Similar to slicing but not quite */
    X                data1(new int(5));
    shared_ptr<int>  data2;
    shared_ptr<int>  data3(data);

    data2 = data1;
}

【讨论】:

  • 只是出于好奇,为什么从 shared_ptr 派生是危险的?
  • @Alex: 因为你把shared_ptr 取为value,而不是引用,因此推导意味着对象切片。想一想:你想必须在堆上分配shared_ptr :) 吗?
【解决方案2】:

只需制作一个包装器对象;容易得多。您可以让包装对象在其中包含一个 shared_ptr 实例,并且仍然使用内部对象的分配地址作为索引。除非我遗漏了什么,否则这似乎比使用派生或自定义清理例程要好得多。

例如:

class CWrapsLuaObject
{
    CWrapsLuaObject( LuaObject* pObject )
    { [assign internal ptr, do mapping, etc.] }

    shared_ptr< LuaObject > m_spObject;

    [...]
};

shared_ptr< CWrapsLuaObject > spInstance( new CWrapsLuaObject( pObject ) );

我是否错过了为什么这不是最简单的解决方案(没有从其他建议的解决方案中拿走任何东西,这也可以工作)?

【讨论】:

    【解决方案3】:

    您可以提供与 shared_ptr 一起使用的自定义删除对象。如果您尝试将额外信息粘贴到 shared_ptr 中,则最好将其放入删除对象中。我觉得它不是很干净,但它确实有效。

    class ExtraThingToDestroy
    {
      public:
       ~ExtraThingToDestroy() { std::cout<<"Destroying the extra thing"<<std::endl; }
    };
    
    template<typename T>
    class CustomDestructor
    {
      public:
        CustomDestructor( ExtraThingToDestroy * v ) : v_(v) {}
        void operator()( T* t ) { delete t; delete v_; }
        ExtraThingToDestroy * v_;
    };
    
    main()
    {
       shared_ptr<int> ptr( new int, MyExtraDestructor<int>( new ExtraThingToDestroy ) );
       shared_ptr<int> ptr2 = ptr;
       //Now when ptr and all its copies get destroyed, 
       // the ExtraThingToDestroy will get deleted along with the int.
    } 
    

    【讨论】:

      【解决方案4】:

      如果你从 shared_ptr 派生类 your_shared_ptr 并覆盖析构函数,你的析构函数应该在这样的代码中调用:

      {
        your_shared_ptr<int> x(new int);
      }
      

      如果你像这样使用它,那么:

      {
        shared_ptr<int>* ptrptr = new your_shared_ptr<int>(new int);
      }
      

      那么它不会,但你真的需要吗?

      还是我误会了什么?

      【讨论】:

      • 我需要调用两个析构函数,这要求 shared_ptr 有一个虚拟析构函数。
      • 不,shared_ptr 的析构函数即使是非虚拟的也会被调用。执行此代码,例如:java2s.com/Code/Cpp/Class/…(使用 Visual C++ 2008 验证)
      • 是的,如果你派生std::shared_ptr,并破坏派生类,share_ptr's 析构函数将被调用。 poliformically销毁时需要虚拟析构函数。
      猜你喜欢
      • 2013-10-24
      • 1970-01-01
      • 2019-04-10
      • 1970-01-01
      • 2012-03-16
      • 2013-09-13
      • 1970-01-01
      • 2020-11-28
      • 1970-01-01
      相关资源
      最近更新 更多