stl源码剖析 详细学习笔记 空间配置器

STL源码剖析(侯杰)——读书笔记 文章目录STL源码剖析(侯杰)——读书笔记1. STL概论2.空间配置器SGI标准空间配置器, std::allocatorSGI特殊的空间配置器, std::alloc构造和析构 construct()和destroy()空间配置与释放3.迭代器概念与traits编程概念迭代器相应型别传递Traits编程技法——STL源码门钥Partial Specialization(偏特化)的意义std::iterator的保证4.序列式容器vector**list**dequestackqueueheappri 阅读详情




//---------------------------15/04/05----------------------------


/*

    空间配置器概述:

    1:new操作包含两个阶段操作

        1>调用::operator new配置内存(底层使用malloc来申请内存)。

        2>调用函数的构造函数,构造对象内容。

    delte和new一样,先调用析构函数,再调用::operator delete释放内存。

    2:为了效率,stl把两个阶段分开来。

        1>内存配置操作: alloc::allocate();

        2>内存释放操作: alloc::deallocate();

        3>对象构造操作: ::construct();

        4>对象析构操作: ::destory();

    3:内存配置器的整体:

        1><stl_construct.h>     : 这里定义了全局函数construct()和的destroy()。

        2><stl_alloc.h>         : 定义了一二级配置器。

        3><stl_uninitialized.h> : 定义了一些全局函数,用来填充或复制大块内存数据。

        un_initialized_copy(), un_initialized_fill(), un_initialized_fill_n()

        这些函数对效率考虑得面面俱到:最差的情况下会调用construct(),最佳的情况下会

        使用c的标准函数memmove()直接进行内存数据的移动。

 

*/


//construct() 和 destroy()


template<class T1, class T2>

inline void construct(T1* p, const T2& value)

{

    new (p) T1(value);

}


template<class T>

inline void destroy(T* pointer)

{

    pointer->~T();

}


template<class ForwardIterator>

inline void destroy(ForwardIterator first, ForwardIterator last)

{

    __destroy(first, last, value_type(first));

}


template<class ForwardIterator, class T>

inline void __destroy(ForwardIterator first, ForwardIterator last, T*)

{

    typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;

    __destroy_aux(first, last, trivial_destructor());

}


template<class ForwardIterator>

inline void

__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type)

{

    for(; first < last; ++first)

        destroy(&*first);

}


template<class ForwardIterator>

inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}


inline void destroy(char*, char*) {}

inline void destroy(wchar_t*, wchar_t*) {}




//alloc


template<class T, class Alloc>

class simple_alloc

{

public:

    static T* allocate(size_t n)

    {

        return 0 == n ? 0 : (T*) Alloc::allocate(n * sizeof(T));

    }

    

    static T* allocate(void)

    {

        return (T*) Alloc::allocate(sizeof(T));

    }

    

    static void deallocate(T *p, size_t n)

    {

        if(0 != n)

            Alloc::deallocate(p, n * sizeof(T));

    }

    

    static void deallocate(T *p)

    {

        Alloc::deallocate(p, sizeof(T));

    }

};


//第一级配置

template<int inst>

class __malloc_alloc_template

{

private:

    static void* oom_malloc(size_t);

    static void* oom_realloc(void *, size_t);

    static void (* __malloc_alloc_oom_handler)();

    

public:

    //直接调用malloc分配内存,失败就调用oom_malloc,这个函数会不断申请分配,

    //并不断调用处理函数,如果没有处理函数,就抛出错误。

    static void* allocate(size_t n)

    {

        void *result = malloc(n);

        if(0 == result)

            result == oom_malloc(n);

        return result;

    }

    

    static void deallocate(void *p, size_t)

    {

        free(p);

    }

    

    static void* reallocate(void *p, size_t, size_t new_sz)

    {

        void *result = realloc(p, new_sz);

        if(0 == result)

            result = oom_realloc(p, new_sz);

        return result;

    }

    

    static void (* set_malloc_handle(void (*f)()))()

    {

        void ( *old)() = __malloc_alloc_oom_handler;

        __malloc_alloc_oom_handler = f;

        return(old);

    }

};


template<int inst>

void ( *__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;



template<int inst>

void* __malloc_alloc_template<inst>::oom_malloc(size_t n)

{

    void ( *my_malloc_handler)();

    void *result;

    

    for(; ;)

    {

        my_malloc_handler = __malloc_alloc_oom_handler;

        if(0 == my_malloc_handler)

        {

            __THROW_BAD_ALLOC;

        }

        (*my_malloc_handler)();

        result = malloc(n);

        if(result)

            return (result);

    }

}


template<int inst>

void* __malloc_alloc_oom_handler<int>::oom_realloc(void *p, size_t n)

{

    void ( *my_malloc_handler());

    void *result;

    for(; ;)

    {

        my_malloc_handler = __malloc_alloc_oom_handler;

        if(0 == my_malloc_handler)

        {

            __THROW_BAD_ALLOC;

        }

        (*my_malloc_handler)();

        result = realloc(p, n);

        if(result)

            return (result);

    }

}


typedef __malloc_alloc_template<0> malloc_alloc;



//第二级配置器


//使用union节省空间

union obj

{

    union obj * free_list_link;

    char client_data[1];

};


enum { __ALIGN = 8};

enum { __MAX_BYTES = 128};

enum { __NFREELISTS = __MAX_BYTES / __ALIGN};


template<bool threads, int inst>

class __default_alloc_template

{

private:

    //这里就是加7再对8取模 这样写的公式扩展性比较小, 也就是__ALIGN必须取2的倍数,

    //不然这公式就不是取模操作了。

    static size_t ROUND_UP(size_t bytes)

    {

        return ((bytes) + __ALIGN - 1) & ~(__ALIGN - 1));

    }

    

    union obj

    {

        union obj *free_list_link;

        char client_data[1];

    };

    

    static obj * volatile free_list[__NFREELISTS];

    static size_t FREELIST_INDEX(size_t bytes)

    {

        return (((bytes) + __ALIGN - 1) / __ALIGN - 1));

    }

    

    static void* refill(size_t n);

    static char *chunk_alloc(size_t size, int &nobjs);

    

    static char *start_free;

    static char *end_free;

    static size_t heap_size;

    

public:

    static void* allocate(size_t n)

    {

        obj * volatile * my_free_list;

        obj * result;

        

        if(n > (size_t)__MAX_BYTES)

            return (malloc_alloc::allocate(n));

        

        my_free_list = free_list + FREELIST_INDEX(n);

        result = *my_free_list;

        if(result == 0)

        {

            void *r = refill(ROUND_UP(n));

            return r;

        }

        *my_free_list = result->free_list_link;

        return (result);

    }

    //如果比最大大小大,就交给第一级配置,否则收回到链表中

    static void deallocate(void *p, size_t n)

    {

        obj *q = (obj*)p;

        obj * volatile * my_free_list;

        if(n > (size_t)__MAX_BYTES)

        {

            malloc_alloc::deallocate(p, n);

            return;

        }

        

        my_free_list = free_list + FREELIST_INDEX(n);

        q->free_list_link = *my_free_list;

        *my_free_list = q;

        

    }

    static void* reallocate(void *p, size_t old_sz, size_t new_sz);

};


template<bool threads, int inst>

char *__default_alloc_template<threads, inst>::start_free = 0;


template<bool threads, int inst>

char *__default_alloc_template<threads, inst>::end_free = 0;


template<bool threads, int inst>

size_t *__default_alloc_template<threads, inst>::heap_size = 0;


template<bool threads, int inst>

__default_alloc_template<threads, inst>::obj *volatile

__default_alloc_template<threads, inst>::free_list[__NFREELISTS] =

{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};



template<bool threads, int inst>

void* __default_alloc_template<threads, inst>::refill(size_t n)

{

    //20个节点

    int nobjs = 20;

    //申请20个节点,会修改nobjs的值,也就是申请到多少个节点

    char *chunk = chunk_alloc(n, nobjs);

    obj * volatile * my_free_list;

    obj *result;

    obj *current_obj, *next_obj;

    int i;

    

    //如果只申请到一个节点,直接返回

    if(1 == nobjs)

        return (chunk);

    //找到n大小节点的位置

    my_free_list = free_list + FREELIST_INDEX(n);

    //第一个节点是用来返回的,不用存入链表中

    result = (obj*)chunk;

    //链表的第一个节点就是下一个节点

    *my_free_list = next_obj = (obj*)(chunk + n);

    for(i = 1; ; i++)

    {

        //当前节点是下一个节点

        current_obj = next_obj;

        //下一个节点是下下个节点

        next_obj = (obj*)((char*)next_obj + n);

        //如果是最后的节点了,就设置下个节点为0,并返回,否则好设置下一个节点

        if(nobjs - 1 == i)

        {

            current_obj->free_list_link = 0;

            break;

        }

        else

        {

            current_obj->free_list_link = next_obj;

        }

    }

    //把申请到的第一个节点返回

    return (result);

}


template<bool threads, int inst>

char* __default_alloc_template<threads, inst>::

chunk_alloc(size_t size, int& nobjs)

{

    char *result;

    //需要的总大小(bytes)

    size_t total_bytes = size * nobjs;

    //内存池中剩余的大小

    size_t bytes_left = end_free - start_free;

    

    //如果剩余的大小大于总大小,直接返回就行了

    if(bytes_left >= total_bytes)

    {

        result = start_free;

        start_free += total_bytes;

        return (result);

    }

    //如果剩下的大小大于一个size, 就返回最大的内存大小

    else if(bytes_left >= size)

    {

        nobjs = bytes_left / size;

        total_bytes = size * nobjs;

        result = start_free;

        start_free += total_bytes;

        return (result);

    }

    //一个都没有

    else

    {

        //算出需要的内存大小,2倍的需求量+现有大小的1/16(需要调整)。

        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);

        //先把内存池中剩余的空间中的内存取出放入链表中

        if(bytes_left > 0)

        {

            obj * volatile * my_free_list =

            free_list + FREELIST_INDEX(bytes_left);

            

            ((obj*)start_free)->free_list_link = *my_free_list;

            *my_free_list = (obj*)start_free;

        }

        //申请内存

        start_free = (char*)malloc(bytes_to_get);

        if(0 == start_free)

        {

            //无法正常申请内存

            int i;

            obj * volatile * my_free_list, *p;

            //尝试拆分大节点

            for(i = size; i <= __MAX_BYTES; i += __ALIGN)

            {

                //找到比要申请的内存大一点的节点,可以把他拆分掉

                my_free_list = free_list + FREELIST_INDEX(i);

                //让p等于这个链表的第一个节点

                p = *my_free_list;

                if(0 != p)

                {

                    //如果有节点,就把这个节点放入内存池中

                    *my_free_list = p->free_list_link;

                    start_free = (char*)p;

                    end_free = start_free + i;

                    return (chunk_alloc(size, nobjs));

                }

            }

            //如果找不到大的节点,只能调用第一级配置,调用客户设置的处理函数了。

            end_free = 0;

            start_free = (char*)malloc_alloc::allocate(bytes_to_get);

        }

        //内存池大小调整

        heap_size += bytes_to_get;

        end_free = start_free + bytes_to_get;

        return (chunk_alloc(size, nobjs));

    }

}







STL源码剖析——空间配置器 前言 空间配置是为存储数据 阅读详情

相关推荐

Effective C++.epub

本书的目的在于引导你如何高效使用 C++。我假设你已经熟悉了作为语言的 C++ 并有使用它的一些经验。我在此提供的是使用这种语言的指南,以使你的程序易于理解,可维护,易移植,可扩展,效率高,而且行为符合你的预期。 wizardforcel. Effective C++ (Kindle 位置 170-172). GitBook. Kindle 版本.

《STL源码剖析》学习笔记——第二章:空间配置器

1. 空间配置器概述 从STL的实现角度来看,空间配置器的位置尤为重要,整个STL的操作对象(所有的数值)都是存放在容器之内,而容器一定需要配置空间以存放资料。空间配置器就是为各个容器提供高效的管理空间。SGI STL的配置器与众不同,也与标准规范不同,其名称为alloc而非allocator,而且不接受任何参数。 不能采用标准写法: vector<int,std::allocator<int>> iv; // inv VC or CB 应该写成: vector<int

baidu_15952103的博客 452

《STL源码剖析》&《深入探索C++对象模型》

该资源包含侯捷的两本书《STL源码剖析》&《深入探索C++对象模型》,讲的都是底层的,讲的很深,也很难(如果你只想用STL,建议不要看这本书),第二本书讲对象讲的很好,比较难。如果想求职程序员,最好还是多啃一下,看明白之后,对C++肯定有质的飞跃~~~~,我也在慢慢啃,虽然啃不动,╮(╯▽╰)╭

stl源码剖析_《STL源码剖析》学习笔记——空间配置器

目录1. 空间配置器概述2. 构造和析构基本工具3. 空间的配置与释放,std::alloc4. 内存基本处理工具1. 空间配置器概述从STL的实现角度来看,空间配置器的位置尤为重要,整个STL的操作对象(所有的数值)都是存放在容器之内,而容器一定需要配置空间以存放资料。空间配置器就是为各个容器提供高效的管理空间。SGI STL的配置器与众不同,也与标准规范不同,其名称为alloc而非alloca...

weixin_39636099的博客 104

effective_C++、STL源码剖析(中文完整版)、深度探索C++对象模型

effective_C++、STL源码剖析(中文完整版)、深度探索C++对象模型 effective_C++、STL源码剖析(中文完整版)、深度探索C++对象模型

stl源码剖析 stl源码剖析

stl源码剖析stl源码剖析stl源码剖析stl源码剖析stl源码剖析stl源码剖析stl源码剖析

《STL源码剖析》学习笔记之二 空间配置器

1.   空间配置器为何称为空间配置器而不是内存配置器呢?因为空间不一定是内存,空间也可以是磁盘或其它辅助存储介质,这意味着你可以写一个allocator,直接向磁盘取空间。1.1.       空间配置器的标准接口 1.1.1.      设计一个简单的空间配置器 1.2.       具备次配置力(sub-allocation)的SGI空间配置器SGI STL的配置

shefg_great的专栏 842

从报错到定位:Memtest86 DQ位算法解析与实践指南

本文深入解析了Memtest86内存测试工具报错信息的解读方法,重点介绍了通过地址解析、数据转换、位比对和DQ定位四步算法,精准锁定故障DQ位的实践流程。文章以DDR4和DDR5为例,详细拆解了从十六进制报错到二进制比对的完整步骤,并提供了从单个报错分析到故障模式诊断的进阶指南,帮助用户将抽象的报错信息转化为具体的维修或排查依据。

sre5engineer的博客 486

《STL源码剖析》学习笔记-第2章 空间配置器

1、SGI 特殊的空间配置器,std::allocSGI STL 的配置器与众不同,也与标准规范不同,其名称是 alloc 而非 allocator ,而且不接受任何参数。我们所习惯的c++内存配置操作和释放操作如下:class Foo { ... }; Foo* pf = new Foo; // 配置内存,然后构造对象 delete pf; // 将对象析构,然后释放内存这其中的new 操作符(n

爱橙子的OK绷的专栏 1510

【CODESYS】-软件Bug与处理方法(20240710更)

【CODESYS】-软件Bug与处理方法(不定期更新)

qq_35316470的博客 5185

从FP16到FP8:我是如何让Stable Diffusion 3.5提速40%而不丢画质的

本文分享了StableDiffusion模型优化的实战经验。通过FP8精度转换、分层处理敏感模块,实现显存占用降低35%、推理速度提升40%。文章详细介绍了提升画质的五大技巧、本地/云端/移动端部署方案,以及二次元模型定制等垂直领域应用。同时总结了硬件兼容性、数据质量等七大常见陷阱,并强调持续监控和伦理考量。作者指出未来应关注MoE架构、注意力机制优化等方向,认为技术优化是持续追求性能与质量平衡的过程。

达则兼济天下 1187

STL空间配置器详解-《STL源码剖析第二章学习笔记》

个人学习笔记,可能有点乱,有理解不对的地方可以给我留言 个人网站www.liujianhua.xyz STL空间配置器:https://www.cnblogs.com/lang5230/p/5556611.html 空间配置器空间配置器概括**alovator的必要接口:****设计一个简单的空间配置器**:只说明个人觉得难理解的部分,源码在《STL源码剖析》p44**set_new_handler(0);** //在配置空间失败时调用参数来进行处理**new(p) T(value);** //pla

qq_51599283的博客 669

KVM环境下Tesla T4/v100 vGPU性能调优手册:显存分配策略与帧率限制解除

本文深入探讨了在KVM虚拟化环境中,针对NVIDIA Tesla T4和V100 GPU进行vGPU性能调优的实战策略。核心内容包括如何根据AI训练、科学计算等不同负载,精细规划显存分配策略,并详细介绍了通过修改sysfs参数解除帧率限制器(FRL)以释放硬件潜力的方法,旨在帮助用户实现虚拟化GPU资源的最大化利用。

cuda7parallel的博客 619

空间配置器(allocator)详解-stl源码剖析学习笔记

空间配置器也就是配置空间,配置容器所需要的空间,该空间获取可以是内存,也可以是磁盘或其他存储介质。

weixin_42853463的博客 1083

卷积神经网络中的自注意力机制(Self-Attention Mechanism)

卷积神经网络中的机制表现为操作,其实现过程与Seq2Seq模型的自注意力机制类似。标准的卷积层是一种局部滤波操作,其输出特征上的任意位置是由对应输入特征的一个邻域构造的,只能捕捉局部特征之间的关系。而自注意力机制通过计算任意两个位置之间的关系直接捕捉远程依赖,而不用局限于相邻点,相当于构造了一个的卷积核,从而可以捕捉更多信息。在卷积网络的自注意力机制中,首先构造输入特征x的键特征fx, 查询特征gx和值特征hx;

AI天才研究院 1501

无线通信技术及其在机器人中的应用

本文系统阐述了无线通信技术在机器人领域的应用框架与技术选型。首先构建了无线通信的三层理论架构:物理层解决信号传输问题,链路层处理接入控制,网络层实现组网管理。随后详细分析了主流无线技术的特性与适用场景:BLE适用于低功耗传感器节点,Wi-Fi适合高带宽视频传输,5G专网满足工业AGV的广域覆盖需求,星闪技术凭借20μs超低时延和4096台并发连接能力,为高端机器人应用提供突破性解决方案。通过鄂尔多斯隆基5G专网等典型案例,验证了5G在工业AGV规模化部署中的优势,并展望了通感算智融合、星闪+5G协同等未来发

weiyvyy的博客 1114

《STL源码剖析》第二章空间配置器学习笔记

SGI标准的空间配置器, std::allocator 这个空间配置器的效率不佳,很少被使用。主要是因为他只是把C++的::operator new和::opeator delete做了一层薄薄的包装而已。大概就这样: template<class T> T* allocator<T>::allocate(size_type n) { if (n == 0) { return nullptr; } return static_cast<T*>(::operat

lx127372的博客 264

情感分析在金融领域的应用:舆情影响股价预测

我们都知道,股价的波动受到多种因素的影响,而大众的情绪和看法就是其中很重要的一部分。我们在日常生活中都有根据别人的评价来判断事物好坏的经验,在金融市场里,舆情就像是大众对某家公司的集体评价,它能反映出市场参与者的情绪和预期。学习这方面的知识,能帮助投资者更好地把握市场动态,做出更明智的投资决策,无论是个人投资者还是金融机构的分析师,都能从中受益。有一天,你看到某家知名企业被曝光了严重的产品质量问题,新闻铺天盖地,股民们也都在热议纷纷,负面情绪弥漫。这时,你会不会觉得这家公司的股价可能要下跌了呢?

AI 原生应用开发的博客 1147

【理论学习/C++】《STL源码剖析》学习笔记:Ch2空间配置器

Ch2 空间适配器 2.1 空间配置器标准接口 必要接口 template<class T> class allcator{ public: typedef T value_type; //基类型 typedef T* pointer; //一级指针 typedef const T* const_pointer; //常指针 typedef T& reference; //引用 typedef const T& const_re

m0_51274130的博客 274

【保姆级教程】基于UNet++&MobileNetv2的语义分割模型从训练到RK3588部署:让边缘AI落地效率提升300%

通过SMP库一键构建encoder_name="mobilenet_v2", # 选择MobileNetv2作为骨干encoder_weights="imagenet", # 加载预训练权重加速收敛in_channels=3, # 输入图像通道数(RGB)classes=29, # Cityscapes数据集类别数activation=None # 输出无激活,便于后续损失计算本教程从“模型设计-训练-转换-边缘部署”全链路拆解了。

FJN110的博客 211

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

STL源码剖析学习笔记之具备次配置力(sub-allocation)的SGI空间配置器

SGI特殊的空间配置器: std::alloc一般而言,我们所习惯的的C++内存配置操作和释放操作是这样的: class Foo {…}; Foo* pf=new Foo;//配置内存,然后构造对象 delete pf; //将对象析构,然后释放内存其中new算式内含两阶段操作: 1.调用 ::operator new 配置内存;template <class T>

吾尝终日而思矣,不如须臾之所学也 794

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

STL源码解析笔记(0)

前言

Linyq29的博客 320

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

《STL源码剖析》学习笔记——空间配置

相信大多数人应该和我一样,对STL的认识是从使用它开始的。但是,这等利器都是双刃剑,不明原理地使用,往往会适得其反。举个例子来说,对vector进行增添或者删除之后,出于内存安全的考虑,原有的迭代器应该摒弃不用。或许,只有真正走进STL的源码,才能解答这些疑问。 《STL源码剖析》这本书的源码来自大名鼎鼎的SGI STL,它是相对来说可读性最高的一个版本。 从实现的角度来看,空间配置器是一切的基

relic911的专栏 451

STL-空间配置器剖析

网上有很多对于STL空间配置器源码的剖析,之所以这么多人去剖析空间配置器,我觉得是真的设计的太好,而且剖析空间配置器的架构的设计对于C++学者来说是一个不错的提高能力的项目,所以加入到这个解剖大军中来。       参照了侯捷的《STL源码剖析》,原本直接看源码不懂得东西,突然间豁然开朗。再次写下自己对于STL空间配置器的一点点理解。    要了解空间配置器,有一张图是必看的:

如风 6498

STL空间配置器allocator详解

stl六大组件简介我们知道,stl有容器,空间配置器,适配器,迭代器,仿函数以及算法这6个组件,它们六者关系大概如下:容器通过配置器取得数据存储空间,算法通过迭代器获取容器内容,仿函数可以协助算法完成不同的策略变化,配接器可以修饰或套界仿函数。侯捷在《STL源码剖析》一书讲到:因此我们需要先去学习空间配置器。预备知识一般来说,我们习惯的C++内存配置和释放操作是这样的:class A {}; A*

xy913741894的博客 1万+
上一篇: stl源码剖析 详细学习笔记 配接器
下一篇: effective c++ 笔记 (18-22)
boydfd
博客等级 码龄16年 101粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值