45

我有我想要的简单基类和派生类shared_from_this()

这个简单的解决方案:

class foo : public enable_shared_from_this<foo> {
    void foo_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&foo::foo_do_it,shared_from_this());
    }
    virtual ~foo() {};
};

class bar1 : public foo , public enable_shared_from_this<bar1> {
    using enable_shared_from_this<bar1>::shared_from_this;
    void bar1_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&bar1::bar1_do_it,shared_from_this());
    }
};

tr1::bad_weak_ptr在以下代码中导致异常:

shared_ptr<foo> ptr(shared_ptr<foo>(new bar1));
function<void()> f=ptr->get_callback();
f();

因此,在“谷歌搜索”之后,我找到了以下解决方案:

class bar2 : public foo {
    void bar2_do_it()
    {
        cout<<"foo::do_it\n";
    }
    shared_ptr<bar2> shared_from_this()
    {
        return boost::static_pointer_cast<bar2>(foo::shared_from_this());
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&bar2::bar2_do_it,shared_from_this());
    }
};

现在它可以工作了。

有没有更好、更方便、更正确的方式对enable_shared_from_this父母和孩子都适用?

谢谢

4

4 回答 4

51

通过在基类上定义以下内容,可以使 OP 解决方案更加方便。

protected:
    template <typename Derived>
    std::shared_ptr<Derived> shared_from_base()
    {
        return std::static_pointer_cast<Derived>(shared_from_this());
    }

这可以通过将其放在基类中(以供重用)来更方便。

#include <memory>

template <class Base>
class enable_shared_from_base
  : public std::enable_shared_from_this<Base>
{
protected:
    template <class Derived>
    std::shared_ptr<Derived> shared_from_base()
    {
        return std::static_pointer_cast<Derived>(shared_from_this());
    }
};

然后从它派生如下。

class foo : public enable_shared_from_base<foo> {
    void foo_do_it()
    {
        std::cout << "foo::do_it\n";
    }
public:
    virtual std::function<void()> get_callback()
    {
        return boost::bind(&foo::foo_do_it, shared_from_base<foo>());
    }
};

class bar1 : public foo {
    void bar1_do_it()
    {
        std::cout << "bar1::do_it\n";
    }
public:
    virtual std::function<void()> get_callback() override
    {
        return boost::bind(&bar1::bar1_do_it, shared_from_base<bar1>());
    }
};
于 2015-08-23T23:18:52.563 回答
16

对不起,但没有。

问题是,shared_ptr<foo>并且shared_ptr<bar1>是不同的类型。我不了解幕后发生的一切,但我认为当构造函数返回并分配给 ashared_ptr<foo>时,内部weak_ptr<bar1>会看到没有任何东西指向它(因为只有 ashared_ptr<bar1>会增加计数器)并自行重置。当你调用bar1::shared_from_thisget_callback,你会得到异常,因为内部weak_ptr没有指向任何东西。

本质上,enable_shared_from_this似乎只能从层次结构中的单个类透明地工作。如果您尝试手动实现它,问题应该会变得很明显。

于 2009-03-21T17:30:30.320 回答
9

如果您想实现一个shared_from_this()函数,@evoskuil 的类似解决方案可以减少派生类中的样板,从而在类中的使用点产生以下代码:

auto shared_from_this() {
    return shared_from(this);
}  

这使用了类之外的“shim”函数。通过这样做,它还为那些接口不能修改但派生自的类提供了一种干净的方法enable_shared_from_this- 例如

auto shared_that = shared_from(that);

注意:auto此处使用 for 返回类型取决于编译器的年龄。

可以放置在库头文件中的 Shim 函数:

template <typename Base>
inline std::shared_ptr<Base>
shared_from_base(std::enable_shared_from_this<Base>* base) 
{
    return base->shared_from_this();
}
template <typename Base>
inline std::shared_ptr<const Base>
shared_from_base(std::enable_shared_from_this<Base> const* base) 
{
    return base->shared_from_this();
}
template <typename That>
inline std::shared_ptr<That>
shared_from(That* that) 
{
    return std::static_pointer_cast<That>(shared_from_base(that));
}

上面的代码依赖于这样一个事实,即传递给的类型shared_from(...)继承自std::enable_shared_from_this<Base>其祖先的某个点。

调用shared_from_base将找出最终是什么类型。由于我们知道That继承自Base,因此可以进行静态向下转换。

可能存在一些带有类型转换运算符的类的病态极端情况。但这不太可能发生在并非旨在打破这种情况的代码中。

例子:

struct base : public std::enable_shared_from_this<base> {};
struct derived : public base
{
    auto shared_from_this() {
        return shared_from(this);
    }
    // Can also provide a version for const:
    auto shared_from_this() const {
        return shared_from(this);
    }
    // Note that it is also possible to use shared_from(...) from
    // outside the class, e.g. 
    // auto sp = shared_from(that);
};
template <typename X>
struct derived_x : public derived
{
    auto shared_from_this() {
        return shared_from(this);
    }
};

编译测试:

int main()
{
    auto pbase = std::make_shared<base>();
    auto pderived = std::make_shared<derived>();
    auto pderived_x = std::make_shared<derived_x<int> >();

    auto const& const_pderived = *pderived;
    const_pderived.shared_from_this();

    std::shared_ptr<base> test1 = pbase->shared_from_this();
    std::shared_ptr<derived> test2 = pderived->shared_from_this();
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this();

    return 0;
}

https://onlinegdb.com/SJWM5CYIG

我发布的先前解决方案一直使评论仍然有意义 - 这将函数放置在有一些问题的基类中 - 特别是“普通”类和模板类所需的实现之间的不一致性。
此外,对于新的类层次结构,需要重复基类中的实现,这并不是那么干燥。此外,通过提供来自不同对象的基类指针,基类函数存在被滥用的可能性。上面的较新方案完全避免了这种情况,并且运行时 assert(...) 检查进行。

旧实现:

#include <cassert>
#include <memory>

class base : public std::enable_shared_from_this<base>
{
protected:   
    template <typename T>
    std::shared_ptr<T> shared_from(T* derived) {
        assert(this == derived);
        return std::static_pointer_cast<T>(shared_from_this());
    }
};

class derived : public base
{
public:
    auto shared_from_this() {
        return shared_from(this);
    }
};

template <typename X>
class derived_x : public derived
{
public:
    auto shared_from_this() {
        return this->template shared_from(this);
    }
};

int main()
{
    auto pbase = std::make_shared<base>();
    auto pderived = std::make_shared<derived>();
    auto pderived_x = std::make_shared<derived_x<int> >();

    std::shared_ptr<base> test1 = pbase->shared_from_this();
    std::shared_ptr<derived> test2 = pderived->shared_from_this();
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this();

    return 0;
}
于 2017-12-13T09:30:50.063 回答
0

很容易; public shared_from_this仅在您的基类中继承。在派生类中实现一个访问器,该访问器转换为适当的类型;

std::shared_ptr<Derived> shared() 
{
   return std::dynamic_pointer_cast<Derived>(Base::shared_from_this());
}
于 2021-08-13T21:14:32.937 回答