浅谈C++11的std::function源码解析

时间:2022-05-29 08:30:32

1、源码准备

本文是基于gcc-4.9.0的源代码进行分析,std::function是C++11才加入标准的,所以低版本的gcc源码是没有std::function的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的。
源码下载地址:http://ftp.gnu.org/gnu/gcc

2、std::function简介

类模版std::function是一种通用的多态函数包装器。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数指针、类成员函数指针(第一个参数需要传入对应的this指针)、Lambda表达式或者某个类的实例(前提是这个类重载了()运算符)。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。

通常std::function是一个函数对象类,它包装其它任意的可调用实体,被包装的对象具有类型为T1,…,TN的N个参数,并且返回一个可转换到R类型的值。std::function使用模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为std::function。最简单的理解就是通过std::function对C++中各种可调用实体的封装,形成一个新的可调用的std::function对象,让我们不再纠结那么多的可调用实体之间如何进行方便高效的转换。

3、源码解析

3.1、std::function解析

std::function位于libstdc++-v3\include\std\functional中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)> : public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>, private _Function_base
{
    typedef _Res _Signature_type(_ArgTypes...);
 
    template<typename _Functor>
    using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())(std::declval<_ArgTypes>()...) );
 
    template<typename _Functor>
    using _Callable = __check_func_return_type<_Invoke<_Functor>, _Res>;
 
    template<typename _Cond, typename _Tp>
    using _Requires = typename enable_if<_Cond::value, _Tp>::type;
 
public:
    typedef _Res result_type;
 
    function() noexcept
        :_Function_base()
    {
    }
 
    function(nullptr_t) noexcept
        :_Function_base()
    {
    }
 
    template<typename _Res, typename... _ArgTypes>
    function(const function& __x)
        :_Function_base()
    {
        if (static_cast<bool>(__x))
        {
            _M_invoker = __x._M_invoker;
            _M_manager = __x._M_manager;
            __x._M_manager(_M_functor, __x._M_functor, __clone_functor);
        }
    }
 
    function(function&& __x)
        :_Function_base()
    { __x.swap(*this); }
 
    template<typename _Functor, typename = _Requires<_Callable<_Functor>, void>>
    function(_Functor __f)
    {
        typedef _Function_handler<_Signature_type, _Functor> _My_handler;
 
        if (_My_handler::_M_not_empty_function(__f))
        {
            _My_handler::_M_init_functor(_M_functor, std::move(__f));
            _M_invoker = &_My_handler::_M_invoke;
            _M_manager = &_My_handler::_M_manager;
        }
    }
 
    function& operator=(const function& __x)
    {
        function(__x).swap(*this);
        return *this;
    }
 
    function& operator=(function&& __x)
    {
        function(std::move(__x)).swap(*this);
        return *this;
    }
 
    function& operator=(nullptr_t)
    {
        if (_M_manager)
        {
            _M_manager(_M_functor, _M_functor, __destroy_functor);
            _M_manager = 0;
            _M_invoker = 0;
        }
        return *this;
    }
 
    template<typename _Functor>
    _Requires<_Callable<_Functor>, function&> operator=(_Functor&& __f)
    {
        function(std::forward<_Functor>(__f)).swap(*this);
        return *this;
    }
 
    template<typename _Functor>
    function& operator=(reference_wrapper<_Functor> __f) noexcept
    {
        function(__f).swap(*this);
        return *this;
    }
 
    void swap(function& __x)
    {
        std::swap(_M_functor, __x._M_functor);
        std::swap(_M_manager, __x._M_manager);
        std::swap(_M_invoker, __x._M_invoker);
    }
 
     explicit operator bool() const noexcept
     { return !_M_empty(); }
 
    _Res operator()(_ArgTypes... __args) const;
    {
        if (_M_empty())
            __throw_bad_function_call();
        return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
    }
 
private:
    typedef _Res (*_Invoker_type)(const _Any_data&, _ArgTypes...);
    _Invoker_type _M_invoker;

从源代码中可以看出以下几点信息:

  • 该类是一个可变参模板类
  • 该类继承于_Maybe_unary_or_binary_function(不分析)和_Function_base,类成员只有_M_invoker一个,从定义可以看出这是一个标准的函数指针
  • 首先分析operator()方法,平时开发中std::function使用最多的肯定就是重载的括号运算符了,毕竟最终也是要把它当成类似于函数的形式来调用的。可以看到operator()函数里面调用了_M_invoker函数,并没有什么特殊的处理
  • 既然_M_invoker能被调用,那就说明它肯定被初始化过了,从调用时传给他的参数来看,多了一个不知道是什么的参数_M_functor,所以我们可以猜测_M_invoker并不是直接指向std::function接管的可调用实体的,而是一个类似中间层的东西,在_M_invoker的实现里面才调用了我们需要执行的那个真实的可调用实体
  • 只有构造函数function(_Functor __f)对_M_invoker进行了初始化,而使用的就是std::_Function_handler里的方法来初始化_M_invoker的,std::_Function_handler的实现在后面会讲到
  • 还是看构造函数function(_Functor __f),因为std::function的目的就是对我们传入的可调用实体进行包装,这里说的可调用实体可以是普通函数指针、类成员函数指针(第一个参数需要传入对应的this指针)、Lambda表达式以及某个类实例(前提是这个类重载了()运算符),而我们看到在std::function这个类里面并没有直接托管我们传入的可调用实体,而只是调用了_My_handler::_M_init_functor(_M_functor, std::move(__f)),推测是由_Function_base来托管可调用实体的

3.2、std::_Function_handler解析

std::_Function_handler位于libstdc++-v3\include\std\functional中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
template<typename _Res, typename _Functor, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), _Functor> : public _Function_base::_Base_manager<_Functor>
{
    typedef _Function_base::_Base_manager<_Functor> _Base;
 
public:
    static _Res _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
    {
        return (*_Base::_M_get_pointer(__functor))(std::forward<_ArgTypes>(__args)...);
    }
};
 
template<typename _Functor, typename... _ArgTypes>
class _Function_handler<void(_ArgTypes...), _Functor> : public _Function_base::_Base_manager<_Functor>
{
    typedef _Function_base::_Base_manager<_Functor> _Base;
 
public:
    static void _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
    {
        (*_Base::_M_get_pointer(__functor))(std::forward<_ArgTypes>(__args)...);
    }
};
 
template<typename _Res, typename _Functor, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), reference_wrapper<_Functor> > : public _Function_base::_Ref_manager<_Functor>
{
    typedef _Function_base::_Ref_manager<_Functor> _Base;
 
public:
    static _Res _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
    {
        return __callable_functor(**_Base::_M_get_pointer(__functor))(std::forward<_ArgTypes>(__args)...);
    }
};
 
template<typename _Functor, typename... _ArgTypes>
class _Function_handler<void(_ArgTypes...), reference_wrapper<_Functor> > : public _Function_base::_Ref_manager<_Functor>
{
    typedef _Function_base::_Ref_manager<_Functor> _Base;
 
public:
    static void _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
    {
        __callable_functor(**_Base::_M_get_pointer(__functor))(std::forward<_ArgTypes>(__args)...);
    }
};
 
template<typename _Class, typename _Member, typename _Res, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), _Member _Class::*> : public _Function_handler<void(_ArgTypes...), _Member _Class::*>
{
    typedef _Function_handler<void(_ArgTypes...), _Member _Class::*> _Base;
 
public:
    static _Res _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
    {
        return std::mem_fn(_Base::_M_get_pointer(__functor)->__value)(std::forward<_ArgTypes>(__args)...);
    }
};
 
template<typename _Class, typename _Member, typename... _ArgTypes>
class _Function_handler<void(_ArgTypes...), _Member _Class::*> : public _Function_base::_Base_manager<_Simple_type_wrapper< _Member _Class::* > >
{
    typedef _Member _Class::* _Functor;
    typedef _Simple_type_wrapper<_Functor> _Wrapper;
    typedef _Function_base::_Base_manager<_Wrapper> _Base;
 
public:
    static bool _M_manager(_Any_data& __dest, const _Any_data& __source, _Manager_operation __op)
    {
        switch (__op)
        {
            #ifdef __GXX_RTTI
            case __get_type_info:
                __dest._M_access<const type_info*>() = &typeid(_Functor);
                break;
            #endif
 
            case __get_functor_ptr:
                __dest._M_access<_Functor*>() = &_Base::_M_get_pointer(__source)->__value;
                break;
 
            default:
                _Base::_M_manager(__dest, __source, __op);
        }
        return false;
    }
 
    static void _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
    {
        std::mem_fn(_Base::_M_get_pointer(__functor)->__value)(std::forward<_ArgTypes>(__args)...);
    }
};

从源代码中可以看出_Function_handler有六种重载形式,以下对其进行分类说明:

  • 第一个和第二个重载形式继承于std::_Function_base::_Base_manager,当std::function接管的可调用实体是一个普通函数指针、类实例或者Lambda表达式时,发挥作用的就是这两个类。里面的内容很简单,通过调用_Function_base::_Base_manager<_Functor>的_M_get_pointer方法从__functor中取出对应的可调用实体,然后直接执行,我们知道能直接执行的可调用实体的类型是普通函数指针、类实例(必须重载了()运算符)或者Lambda表达式(Lambda其实本质就是一个匿名的类实例)。这两个重载形式的唯一区别就是它们一个处理函数有返回值的情况,另一个处理没返回值的情况。
  • 第三个和第四个重载形式继承于std::_Function_base::_Ref_manager,可以看出它们基本上算是前两个类的偏特化版本,当第二个模板参数为std::reference_wrapper包装的引用时,就调用这两个偏特化的版本。这两个重载形式的唯一区别也是它们一个处理函数有返回值的情况,另一个处理没返回值的情况。现在问题来了,为什么要搞一个对于std::reference_wrapper类型的偏特化版本呢?这是因为如果可调用实体已经先被std::reference_wrapper包装过的话,那我们是绝对绝对不能直接调用这个可调用实体的,因为此时根本不确定这个被包装的可调用实体究竟是什么类型的,如果是类成员函数的话那当然是不能直接调用的,此时必须使用std::mem_fn来获取一个可调用的对象,类中使用的std::__callable_functor函数的实现如下面的代码所示,可以看到有好几种特化版本,当std::reference_wrapper包装的可调用实体是一个类成员函数指针时,就会通过std::mem_fn来获取一个可调用的对象,这和前面描述的内容一致。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename _Functor>
inline _Functor& __callable_functor(_Functor& __f)
{ return __f; }
 
template<typename _Member, typename _Class>
inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* &__p)
{ return std::mem_fn(__p); }
 
template<typename _Member, typename _Class>
inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* const &__p)
{ return std::mem_fn(__p); }
 
template<typename _Member, typename _Class>
inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* volatile &__p)
{ return std::mem_fn(__p); }
 
template<typename _Member, typename _Class>
inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* const volatile &__p)
{ return std::mem_fn(__p); }

关于上面提到的std::reference_wrapper和std::mem_fn,大家如果可以不懂的话一定要看下面两篇文章,不然的话就像学英语不会英语单词一样,根本不可能看懂std::function的内容的

C++11的std::ref、std::cref源码解析
C++11的std::mem_fn源码解析

  • 第五个和第六个重载形式和前面的差不多,这两个也是属于偏特化版本,主要是用于处理可调用实体为类成员函数指针的情况,这里就可以看到直接调用了std::men_fn函数来使得我们可以直接调用对应的类成员函数,从这点也可以看出std::men_fn函数的重要性,不懂的小伙伴一定要去看前面两篇文章啊。
  • 每一个类里面的_M_invoke函数都用了_M_get_pointer(来源不全部相同),从代码逻辑上不难看出_M_get_pointer函数的作用是从第一个传入参数__functor中取出对应的可调用实体,然后将后面的可变参传给这个可调用实体,运行它,这个功能是不是就有点似曾相识了?对,这就是我们平时正常调用函数的那样子嘛,也就是说std::function的函数执行功能在这里实现了
  • 从代码中我们可以看出这几个类都是和std::_Function_base相关的,并且到现在还是不知道_M_functor究竟是个什么东西,接下来分析std::_Function_base,看一下里面究竟做了哪些工作

3.3、_Any_data解析

_Any_data位于libstdc++-v3\include\std\functional中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
union _Nocopy_types
{
    void*       _M_object;
    const void* _M_const_object;
    void (*_M_function_pointer)();
    void (_Undefined_class::*_M_member_pointer)();
};
 
union _Any_data
{
    void*       _M_access()       { return &_M_pod_data[0]; }
    const void* _M_access() const { return &_M_pod_data[0]; }
 
    template<typename _Tp>
    _Tp& _M_access()
    { return *static_cast<_Tp*>(_M_access()); }
 
    template<typename _Tp>
    const _Tp& _M_access() const
    { return *static_cast<const _Tp*>(_M_access()); }
 
    _Nocopy_types _M_unused;
    char _M_pod_data[sizeof(_Nocopy_types)];
};

看std::_Function_base之前先看一个重要的联合体_Any_data,这个在前面出现很多次了,但是一直没有介绍一下它究竟是个什么东西,下面简单分析一下:

  • 有两个联合体成员,一个是_M_unused(没卵用),一个是_M_pod_data,这两个的占用内存是一样的,具体原因就不讲了,大家可以自己用sizeof去试一下
  • 里面有四个_M_access函数,前两个是直接将_M_pod_data的地址返回出去,不做任何转换,后两个则是可以将_M_pod_data转换为任意类型返回,从这里可以看出这个_Any_data的作用就是来接管可调用对象的,所以后续可以通过各种转换将它还原为可调用的形式(比如前面提到的那个_Function_base::_Base_manager<_Functor>::_M_get_pointer方法就是干这个货活的)
  • 简单看一下_Nocopy_types这个联合体,前两个成员的寓意就是类实例或Lambda表达式,第三个寓意是普通函数指针,第四个寓意是类成员函数指针,仔细一看这不就是我们前面提到无数次的可调用实体的几种形式吗?这个_Nocopy_types从上下文看来并没有什么乱用,估计就是源码作者写给读者看的吧,让大家更容易读懂源码。

3.4、std::_Function_base解析

std::_Function_base的实现位于libstdc++-v3\include\std\functional中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
class _Function_base
{
public:
    static const std::size_t _M_max_size = sizeof(_Nocopy_types);
    static const std::size_t _M_max_align = __alignof__(_Nocopy_types);
 
    template<typename _Functor>
    class _Base_manager
    {
    protected:
        static const bool __stored_locally =
            (__is_location_invariant<_Functor>::value
            && sizeof(_Functor) <= _M_max_size
            && __alignof__(_Functor) <= _M_max_align
            && (_M_max_align % __alignof__(_Functor) == 0));
 
        typedef integral_constant<bool, __stored_locally> _Local_storage;
 
        static _Functor* _M_get_pointer(const _Any_data& __source)
        {
            const _Functor* __ptr = __stored_locally? std::__addressof(__source._M_access<_Functor>()) : __source._M_access<_Functor*>();
            return const_cast<_Functor*>(__ptr);
        }
 
        static void _M_clone(_Any_data& __dest, const _Any_data& __source, true_type)
        {
            new (__dest._M_access()) _Functor(__source._M_access<_Functor>());
        }
 
        static void _M_clone(_Any_data& __dest, const _Any_data& __source, false_type)
        {
            __dest._M_access<_Functor*>() = new _Functor(*__source._M_access<_Functor*>());
        }
 
        static void _M_destroy(_Any_data& __victim, true_type)
        {
            __victim._M_access<_Functor>().~_Functor();
        }
 
        static void _M_destroy(_Any_data& __victim, false_type)
        {
            delete __victim._M_access<_Functor*>();
        }
 
    public:
        static bool _M_manager(_Any_data& __dest, const _Any_data& __source, _Manager_operation __op)
        {
            switch (__op)
            {
                case __get_functor_ptr:
                    __dest._M_access<_Functor*>() = _M_get_pointer(__source);
                    break;
 
                case __clone_functor:
                    _M_clone(__dest, __source, _Local_storage());
                    break;
 
                case __destroy_functor:
                    _M_destroy(__dest, _Local_storage());
                    break;
            }
            return false;
        }
 
        static void _M_init_functor(_Any_data& __functor, _Functor&& __f)
        { _M_init_functor(__functor, std::move(__f), _Local_storage()); }
 
        template<typename _Signature>
        static bool _M_not_empty_function(const function<_Signature>& __f)
        { return static_cast<bool>(__f); }
 
        template<typename _Tp>
        static bool _M_not_empty_function(_Tp* const& __fp)
        { return __fp; }
 
        template<typename _Class, typename _Tp>
        static bool _M_not_empty_function(_Tp _Class::* const& __mp)
        { return __mp; }
 
        template<typename _Tp>
        static bool _M_not_empty_function(const _Tp&)
        { return true; }
 
    private:
        static void _M_init_functor(_Any_data& __functor, _Functor&& __f, true_type)
        { new (__functor._M_access()) _Functor(std::move(__f)); }
 
        static void _M_init_functor(_Any_data& __functor, _Functor&& __f, false_type)
        { __functor._M_access<_Functor*>() = new _Functor(std::move(__f)); }
    };
 
    template<typename _Functor>
    class _Ref_manager : public _Base_manager<_Functor*>
    {
        typedef _Function_base::_Base_manager<_Functor*> _Base;
 
    public:
        static bool _M_manager(_Any_data& __dest, const _Any_data& __source, _Manager_operation __op)
        {
            switch (__op)
            {
                case __get_functor_ptr:
                    __dest._M_access<_Functor*>() = *_Base::_M_get_pointer(__source);
                    return is_const<_Functor>::value;
                    break;
 
                default:
                    _Base::_M_manager(__dest, __source, __op);
            }
            return false;
        }
 
        static void _M_init_functor(_Any_data& __functor, reference_wrapper<_Functor> __f)
        {
            _Base::_M_init_functor(__functor, std::__addressof(__f.get()));
        }
    };
 
    _Function_base() : _M_manager(0) { }
 
    ~_Function_base()
    {
        if (_M_manager)
            _M_manager(_M_functor, _M_functor, __destroy_functor);
    }
 
    bool _M_empty() const { return !_M_manager; }
 
    typedef bool (*_Manager_type)(_Any_data&, const _Any_data&, _Manager_operation);
 
    _Any_data     _M_functor;
    _Manager_type _M_manager;
};

从源代码中可以看出以下几点信息:

  • 该类有两个类成员,分别是_M_functor和_M_manager,_M_functor就不用多说了,前面一直有提到它,他的类型_Any_data也在上一小节讲过了。而_M_manager则是一个函数指针,下面再介绍它有什么用。
  • 这里看一下_Function_base::_Base_manager类里面的各个方法
    • _M_init_functor函数:该函数前面提到过了,当时看到的是将std::function接管的可调用对象传递给了这个函数,而从前面我们知道_Any_data类型的数据是可以接管所有形式的可调用实体的,所以综合可得_M_init_functor函数的作用就是将传递给它的第二个参数储存到第一个参数中(_Any_data类型数据),这样就达成了接管可调用实体的功能了
    • _M_get_pointer函数:这个函数同样在前面提到过,当时我们只知道通过调用_M_get_pointer可以获取到我们接管的那个可调用实体,但是不知道是如何实现的,这里可以看出他的实现是非常简单的,也就是从它的传入参数(_Any_data类型数据)将可调用实体取出,并强制转换为合法的类型,里面用到了std::__addressof,作用就是即使目标变量(类)存在operator&的重载,也依然能够获得变量的地址。
    • 关于std::__addressof的详细内容可以看这篇文章《C++11的std::addressof源码解析
    • _M_manager函数:该函数就是根据传入的第三个参数来确定执行不同的功能,其余的几个函数无非就是涉及到内存的分配和释放之类的,对我们理解std::function的影响不大,这里就不展开讲了
  • 接下来看一下_Function_base::_Ref_manager类
    • 可以看到该类继承于_Function_base::_Base_manager类,可见他拥有_Function_base::_Base_manager类实现的所有功能
    • 该类是处理当std::function接管的是一个引用包装类的情况的,为什么这种情况需要单独处理呢?因为此时如果直接对传入参数取地址,取到的将是引用包装类的地址,而不是我们要接管的可调用对象的地址,所以只能搞这么一个特化版本,就像_Function_base::_Ref_manager类做的那样,里面的_M_init_functor函数通过使用reference_wrapper的get方法获取到了std::function接管的可调用对象的真实地址
  • _Function_base类里的其它方法就不讲了,大家自己看一下吧,其余的实现基本都是基于前面讲的那些内容,比较简单

4、总结

本文先是简单介绍了std::function的用途(对C++中各种可调用实体进行封装),然后通过对源码进行详细分析,我们知道了std::function是如何实现对可调用实体进行封装的,源码内容会比较复杂,但是其中的设计思路是很值得我们学习借鉴的,尤其是与std::reference_wrapper和std::mem_fn配合的那部分代码更是精妙绝伦。
读者如果不清楚std::reference_wrapper和std::mem_fn的作用的话,是不可能完全看懂std::function的源代码的,所以这里再次建议大家先看完下面两篇文章再来学习std::function相关内容:

C++11的std::ref、std::cref源码解析
C++11的std::mem_fn源码解析

到此这篇关于浅谈C++11的std::function源码解析的文章就介绍到这了,更多相关C++11 std::function内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_43798887/article/details/116571325