模糊指向堆或堆栈对象的智能指针



我的一个应用程序将极大地受益于std::unique_ptr<T>的一个变体,它可以配置为不总是假定所指向的对象的所有权。

考虑以下类层次结构:

class AbstractFoo { ... };
template<typename T> Foo : public AbstractFoo 
{
    Foo( const AbstractFoo& absFoo ) { ... } 
    ... 
};

和一个API,它对每个接受AbstractFoo的例程进行标准化,并在必要时转换为Foo<T>的特定实例。在对AbstractFoo的引用实际上已经是适当派生类型的实例的情况下,只需要dynamic_cast,不需要复制数据。但是,当抽象引用的类型不正确时,需要执行一些重要的工作来创建请求格式的副本。

我想要的界面如下所示:

template<typename T>
my_unique_ptr<Foo<T>> Convert( AbstractFoo& absFoo )
{
    if( Foo<T>* foo = dynamic_cast<Foo<T>*>(&absFoo) )
        return my_unique_ptr<Foo<T>>( foo, false );
    else
        return my_unique_ptr<Foo<T>>( new Foo<T>(absFoo) );
}
void Bar( AbstractFoo& absFoo )
{
    my_unique_ptr<Foo<T>> ptr = Convert<T>( absFoo );
    ...
}

make_unique_ptr<T>有一个类似于std::unique_ptr<T>的构造函数,但有一个可选的布尔参数,指定指针是否应该由智能指针拥有。

对于这种情况是否有最佳实践解决方案?我宁愿避免返回原始指针,因为如果在手动删除对象之前抛出异常,可能会导致内存泄漏。

您可以将shared_ptr与自定义删除器结合使用:

template<typename T>
shared_ptr<Foo<T>> Convert( AbstractFoo& absFoo )
{
    if( Foo<T>* foo = dynamic_cast<Foo<T>*>(&absFoo) )
        return shared_ptr<Foo<T>>( foo, [](Foo<T>*){} ); // do-nothing deleter
    else
        return make_shared<Foo<T>>( absFoo ); // regular deleter
}

Update: programmerjake显然在我打字的时候在评论中写了同样的想法。如果你想把它作为答案写下来,我就把我的删掉。

我认为这个设计是破碎的脆弱的:

template<typename T>
my_unique_ptr<Foo<T>> Convert( AbstractFoo& absFoo )
{
    if( Foo<T>* foo = dynamic_cast<Foo<T>*>(absFoo) )
        return my_unique_ptr<Foo<T>>( foo, false );
    else
        return my_unique_ptr<Foo<T>>( new Foo<T>(absFoo) );
}

if路径中,创建一个对象,该对象的absFoo参数的生存期相关联。

else路径中,您创建的对象与任何其他对象的生命周期无关。

调用者无法区分这两种情况——这似乎相当脆弱。


至于仍然使用它(与shared_ptr如dlf建议)…也许Convert可以被称为smart_foo_cast或类似的东西,那么生命周期的事情在它的名字中会更好地体现出来。

就我个人而言,我也会让它采取AbstractFoo*(这种变化不会影响外部API)。只要确保永远不要使用const AbstractFoo&,因为您永远不知道const&何时可能是隐式临时的。


你的问题就会消失,如果你可以(可选)通过使用unique_ptr作为参数来"接收"参数,或者"共享"参数。

// Caller always yields ownership of absFoo:
template<typename T>
unique_ptr<Foo<T>> Convert( unique_ptr<AbstractFoo> absFoo );
// Caller may yield ownership of absFoo:
// (Caller needs to check whether absFoo was moved-from)
template<typename T>
unique_ptr<Foo<T>> Convert( unique_ptr<AbstractFoo>& absFoo );
// Caller may share ownership of absFoo with return value:
template<typename T>
shared_ptr<Foo<T>> Convert( const shared_ptr<AbstractFoo>& absFoo );

相关内容

  • 没有找到相关文章

最新更新