通过继承扩展 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
的要点),虚拟析构函数不是这真的不是问题。
不过,可能会出现一些互操作性问题。
只有在创建派生的特定实例时才会得到派生的 class。当你从 get_shared_from_this
之类的地方得到 shared_ptr
时,它不会包括你的 info
.
在 shared_ptr<T>
上重载的函数模板将看不到继承。您派生的 class 会突然对 std::static_pointer_cast
.
等随机函数显得陌生
幸运的是,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()
。
做这样的事情有什么不好?
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
的要点),虚拟析构函数不是这真的不是问题。
不过,可能会出现一些互操作性问题。
只有在创建派生的特定实例时才会得到派生的 class。当你从
get_shared_from_this
之类的地方得到shared_ptr
时,它不会包括你的info
.在
shared_ptr<T>
上重载的函数模板将看不到继承。您派生的 class 会突然对std::static_pointer_cast
. 等随机函数显得陌生
幸运的是,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()
。