通过继承扩展 shared_ptr

extending shared_ptr by inheritance

做这样的事情有什么不好?

class myclass : public std::shared_ptr<myotherclass> {
     // some code,  an allocation is never done
     std::string get_info () {
          if(*this != nullptr) return "<info>" + (this->info * 3) + "</info>";
          else return "";
     }
 };

当class中没有分配时---只是像上面那样提供一些装饰?

原则上允许从STL派生classes,见here and here。但是,您必须知道,您不应该使用指向基数 class 的指针——即本例中的 std::shared_ptr<myotherclass>*

所以应该禁止这个及其变体:

std::shared_ptr<myotherclass>* ptr = new myclass(/* ... */);

...但同意,这看起来有点合成。

为什么禁止?因为 STL classes 没有虚拟析构函数。因此,当您想要 delete 您分配的 class 时,派生部分仍然存在。这反过来会调用 undefined behaviour 并可能造成内存泄漏——即使您在派生的 class.

中没有一些分配

为了做到这一点,一种可能性是从 shared_ptr:

私下派生
class myclass : private std::shared_ptr<myotherclass> {};
                ^^^^^^^

但这可能会带来二进制兼容性问题,请参阅 this answer 的评论。

另一方面,即使前者是允许的,您也可以继续使用组合,这样就不会那么容易出错,您可以让 shared_ptr 成为 myclass 的成员并公开所需的功能(缺点是您有时不得不暴露很多)。或者你可以设置一个独立的函数来做你想做的事......我知道你知道 ;-)

因为你永远不会手动 delete 它(你永远不应该手动 delete 任何东西,这首先是 shared_ptr 的要点),虚拟析构函数不是这真的不是问题。

不过,可能会出现一些互操作性问题。

  1. 只有在创建派生的特定实例时才会得到派生的 class。当你从 get_shared_from_this 之类的地方得到 shared_ptr 时,它不会包括你的 info.

  2. shared_ptr<T> 上重载的函数模板将看不到继承。您派生的 class 会突然对 std::static_pointer_cast.

  3. 等随机函数显得陌生

幸运的是,C++ 标准库充满了整洁的可扩展性挂钩。您可以像这样安装自定义删除器:

template< typename t >
struct my_deleter
    : std::default_delete< t > {
    std::string info;

    my_deleter( std::string in_info )
        : info( std::move( in_info ) ) {}
};

std::shared_pointer< foo > myfoo( new foo, my_deleter{ "it's a foo" } );

并使用非成员函数检索信息:

template< typename t >
std::string get_my_info( std::shared_ptr< t > ptr ) {
    my_deleter< t > * dp = std::get_deleter< my_deleter< t > >( ptr );
    if ( ! dp ) return {};
    return dp->info;
}

这不是一个很好的程序架构,因为每个共享对象只有一个自定义删除器插槽。不过,它可以在紧要关头完成。

我建议使用 std::enable_shared_from_this<> 然后 this->shared_from_this()