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

时间:2014-08-15 20:11:17

标签: c++ c++11 smart-pointers

我的一个应用程序将从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>的构造函数,但是带有一个可选的boolean参数,用于指定指针是否应归智能指针所有。

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

2 个答案:

答案 0 :(得分:4)

您可以将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
}

更新:programmerjake显然在我输入时在评论中写了相同的想法。如果你想把它写成答案,我会删除我的。

答案 1 :(得分:2)

我认为这个设计是 broken 脆弱:

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_ptrConvert一样)... 也许smart_foo_cast可以被称为AbstractFoo*或者某些人,那么一生中的东西就会更明显地显示出来。

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


如果你可以(可选)&#34;沉没&#34;你的问题就会消失。使用// 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 ); 作为arg或&#34;分享&#34;论证。

{{1}}