c++ 如何启用父代和派生的_shared_from_this

5gfr0r5j  于 2023-06-07  发布在  其他
关注(0)|答案(6)|浏览(149)

我有一个简单的基类和派生类,我希望它们都有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();

所以在“google”之后,我找到了以下解决方案:

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的父母和孩子?
谢谢

9vw9lbht

9vw9lbht1#

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());
    }
};

然后如下导出。

#include <functional>
#include <iostream>

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 std::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 std::bind(&bar1::bar1_do_it, shared_from_base<bar1>());
    }
};
63lcw9qa

63lcw9qa2#

抱歉,没有。
问题是shared_ptr<foo>shared_ptr<bar1>是不同的类型。我并不理解这背后发生的一切,但我 * 认为 * 当构造函数返回并被赋值给shared_ptr<foo>时,内部的weak_ptr<bar1>会发现没有任何东西指向它(因为只有shared_ptr<bar1>会递增计数器)并重置自己。当您在get_callback中调用bar1::shared_from_this时,您会得到异常,因为内部weak_ptr没有指向任何东西。
从本质上讲,enable_shared_from_this似乎只能从层次结构中的单个类透明地工作。如果您尝试手动实现它,问题应该会变得很明显。

zed5wv10

zed5wv103#

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

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

这在类外部使用了“shim”函数。通过这种方式,它也为那些接口不能修改但从enable_shared_from_this派生的类提供了一种干净的方法。

auto shared_that = shared_from(that);

注意:在这里使用auto作为返回类型将取决于编译器的年龄。
可以放置在库头中的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
我发布的之前的解决方案,保持注解仍然有意义-这将函数放在基类中,这有一些问题-特别是“正常”类和模板类所需的实现之间的不一致性。
此外,对于新的类层次结构,需要重复基类中的实现,这并不完全是DRY。此外,基类函数可能会因提供来自不同对象的基类指针而被误用。上面较新的方案完全避免了这一点,并且运行时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;
}
5hcedyr0

5hcedyr04#

非常简单;只在基类中继承public shared_from_this。在派生类中实现强制转换为适当类型的访问器;

std::shared_ptr<Derived> shared() 
{
   return std::dynamic_pointer_cast<Derived>(Base::shared_from_this());
}
kqlmhetl

kqlmhetl5#

template<class T>
class Base : public std::enable_shared_from_this<T> {
};

template<class T>
class Intermediate : public Base<T> {
};

class Derived : public Intermediate<Derived> {
  public:

  std::shared_ptr<Derived> getDerived() { return shared_from_this(); }
};
dojqjjoe

dojqjjoe6#

使用c++23推导出这一点,事情变得容易得多。https://godbolt.org/z/j499WK58Y

#include <memory>
#include <iostream>
#include <functional>
using namespace std;

struct new_enable_shared_from_this :
    public std::enable_shared_from_this<new_enable_shared_from_this> {
    
    template <typename Self>
    auto new_shared_from_this(this Self& self) {
        return std::static_pointer_cast<Self>(self.shared_from_this());
    }
};

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

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

int main() {
    auto pf = std::make_shared<foo>();
    pf->get_callback()();
    auto pb = std::make_shared<bar1>();
    pb->get_callback()();
}

这是以前的答案:
我不喜欢虚拟函数。虚函数只是用于类型擦除,但我们并不总是需要类型擦除。因此,提供一种类型擦除机制就足够了。下面是一个不使用虚函数的例子:

#include <iostream>
#include <functional>
#include <memory>
using namespace std;

template<typename derived>
class foo_imp {
    void foo_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    function<void()> get_callback()
    {
        auto&& d = static_cast<derived&>(*this);
        return bind(&foo_imp::foo_do_it, d.shared_from_this());
    }
};

template<typename derived>
class bar_imp {
    void bar_do_it()
    {
        cout<<"bar::do_it\n";
    }
public:
    function<void()> get_callback()
    {
        auto&& d = static_cast<derived&>(*this);
        return bind(&bar_imp::bar_do_it, d.shared_from_this());
    }
};

struct foo : public foo_imp<foo>, public enable_shared_from_this<foo> {};
struct bar : public bar_imp<bar>, public enable_shared_from_this<bar> {};

struct v_foo {
    virtual function<void()> get_callback() = 0;
};

template <typename T>
std::shared_ptr<v_foo> convert(const std::shared_ptr<T>& st) {
    struct _ : public v_foo {
        _(const std::shared_ptr<T>& st) : _st{st} {}
        function<void()> get_callback() override {
            return _st->get_callback();
        }
        std::shared_ptr<T> _st;
    };
    return std::make_shared<_>(st);
}

int main() {
    auto sf = make_shared<bar>();
    sf->get_callback()();

    auto svf = convert(sf);
    svf->get_callback()();

    auto sb = make_shared<foo>();
    sb->get_callback()();

    auto svb = convert(sb);
    svb->get_callback()();
}

相关问题