C++11异常和智能指针

异常机制下防止内存泄漏的操作

场景1

void Func()
{
	int* pointer = new int[10];

	int left, right;
	cin >> left >> right;

	try
	{
		cout << Division(left, right) << endl;
	}
	catch (...) 
	{
		cout << "delete []" << pointer << endl;
		delete[] pointer;

		throw;   
	}
	
	cout << "delete []" << pointer << endl;
	delete[] pointer;
}

场景2

int div()
{
    int a, b;
    cin >> a >> b;
    if (b == 0)
    {
        throw invalid_argument("Division by zero condition!");
    }

    return a / b;
}
void Func()
{
    int* p1 = new int;
    int* p2 = new int;

    cout << div() << endl;

    cout << "delete[] " << p1 << endl;
    cout << "delete[] " << p2 << endl;
    delete p1;
    delete p2;
}
int main()
{
    try
    {
        Func();
    }
    catch (const exception& e)
    {
        cout << e.what() << endl;
    }

    return 0;
}

自实现智能指针SmartPtr

template <class T>
class SmartPtr
{
    public:
    // RAII 思想
    SmartPtr(T* ptr = nullptr)
        :_ptr(ptr)
        {}

    ~SmartPtr()
    {
        cout << "~SmartPtr()->" << _ptr << endl;

        if (_ptr)
            delete _ptr;
    }

    // 让 SmartPtr 具有指针的特性
    T& operator*()
    {
        return *_ptr;
    }

    T* operator->()
    {
        return _ptr;
    }

    private:
    T* _ptr;
};
void TestSmartPtr1()
{
    SmartPtr<int> sp1(new int);
    *sp1 = 10;

    SmartPtr<pair<string, int>> sp2(new pair<string, int>("abcde", 1));

    // sp2.operator->()->first
    cout << sp2->first << endl;

    // sp2.operator->()->second
    cout << sp2->second << endl;
}

智能指针拷贝问题

void TestSmartPtr2()
{
    SmartPtr<int> sp1(new int);
    
    sp1 = sp1; 					// 自己给自己赋值 	 this->_ptr == sp1._ptr)
    SmartPtr<int> sp2 = sp1;	// 自己给别人赋值 	 this->_ptr != sp1._ptr)
    sp2 = sp1;					// 自己给“自己”赋值 this->_ptr == sp1._ptr)
}

auto_ptr:转移管理权 导致悬空指针

unique_ptr:禁止拷贝 只允许唯一指针

shared_ptr

std库的共享指针是线程安全的

td::shared_ptr是线程安全的。‌

std::shared_ptr的引用计数操作是线程安全的,‌这意味着多个线程可以同时访问和修改引用计数,‌而不会导致竞争条件。‌

std::shared_ptr的引用计数存储在堆上的控制块中,‌多个std::shared_ptr对象指向同一个堆地址,‌std::shared_ptr的引用计数更新操作被设计为原子操作,‌确保了在多线程环境下的正确性,因此在进行计数的增加或减少时,‌能够保证线程安全。‌

需要注意的是,‌虽然std::shared_ptr本身及其引用计数的更新是线程安全的,‌但它并不保证所指向对象的线程安全。‌如果多个线程需要同时访问或修改该对象,‌那么必须采取额外的同步措施,‌如使用互斥锁(‌mutex)‌,‌以确保对共享数据的访问是线程安全的。‌

不考虑线程安全的共享指针

namespace LHR
{
    template <class T>
        class shared_ptr
        {
            T* _ptr;
            int* _pcount; // 引用计数的指针

            public:
            shared_ptr(T* ptr)
                : _ptr(ptr)
                    , _pcount(new int(1)) // 注意:在构造时申请引用计数的空间,为每一份资源绑定一个引用计数
                {
                }

            void Release()
            {
                //cout << "void Release()" << endl;
                if (--(*_pcount) == 0 && _ptr != nullptr)
                {
                    cout << "delete _ptr: " << _ptr << endl;
                    delete _ptr;
                    delete _pcount;
                }
            }

            void AddCount()
            {
                ++(*_pcount);
            }

            shared_ptr(const shared_ptr& sp)
                : _ptr(sp._ptr),
            _pcount(sp._pcount)
            {
                AddCount();
            }


            // 左 = 右 this = sp; 赋值后 
            // 左指针this指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况
            // 右指针sp指向空间的指针多了一个 右指针的count++
            shared_ptr& operator=(const shared_ptr& sp)
            {
                if (_ptr != sp._ptr)
                {
                    Release();
                    _ptr = sp._ptr;
                    _pcount = sp._pcount;
                    AddCount();
                }
                return *this;
            }

            ~shared_ptr()
            {
                Release();
            }

            T* get()
            {
                cout << "get _ptr: ";
                return _ptr;
            }
            T& operator*()
            {
                return *_ptr;
            }

            T* operator->()
            {
                return _ptr;
            }
            int use_count()
            {
                return *_pcount;
            }
        };
}

class Date
{
    int _year = 0;
    int _month = 0;
    int _day = 0;
};

void ThreadFunc(LHR::shared_ptr<Date>& sharedPtr, int n)
{
    while (n--)
    {
        // 两个线程并发进行n次拷贝构造,即n次引用计数的++和--,存在线程安全问题
        LHR::shared_ptr<Date> copy_sharedPtr(sharedPtr);
        // 拷贝构造 sharedPtr指向的空间的count++
        // 局部域结束 copy_sharedPtr析构 sharedPtr指向的空间的count--
    }
}

int main()
{
    LHR::shared_ptr<Date> sharedPtr(new Date);
    cout << sharedPtr.get() << endl;

    int n = 100000;
    // 多线程执行
    thread t1(ThreadFunc, ref(sharedPtr), n); //线程函数传参传引用需要使用ref
    thread t2(ThreadFunc, ref(sharedPtr), n);

    t1.join();
    t2.join();

    // 最后打印sp的引用计数
    cout << "sharedPtr.use_count(): " << sharedPtr.use_count() << endl;
    return 0;
}

在这里插入图片描述

由于线程安全问题导致引用计数count本应减到0现在没到0 以至于最后改空间没有被释放!

添加互斥锁后的共享指针

namespace LHR
{
    template <class T>
        class shared_ptr
        {
            T* _ptr;
            int* _pcount; // 引用计数的指针
            mutex* _mtx; // 互斥锁的指针
            public:
            shared_ptr(T* ptr)
                : _ptr(ptr)
                    , _pcount(new int(1))   // 注意:在构造时申请引用计数的空间,为每一份资源绑定一个引用计数
                    , _mtx(new mutex)       // 注意:在构造时申请互斥量的空间,为每一份资源绑定一个互斥量
                {

                }

            void Release()
            {
                bool delete_flag = false;

                //加锁保护引用计数
                _mtx->lock();
                if (--(*_pcount) == 0 && _ptr != nullptr)
                {
                    cout << "delete _ptr: " << _ptr << endl;
                    delete _ptr;
                    delete _pcount; // 记得释放引用计数的空间
                    delete_flag = true;
                }
                _mtx->unlock();
                //必须先解锁再销毁互斥锁,因此:
                if (delete_flag)
                {
                    delete _mtx; // 记得释放互斥量的空间
                }
            }

            void AddCount()
            {
                //加锁保护引用计数
                _mtx->lock();
                ++(*_pcount);
                _mtx->unlock();
            }

            shared_ptr(const shared_ptr& sp)
                : _ptr(sp._ptr),
            _pcount(sp._pcount),
            _mtx(sp._mtx) // 记得拷贝互斥量指针
            {
                AddCount();
            }

            shared_ptr& operator=(const shared_ptr& sp)
            {
                if (_ptr != sp._ptr)
                {
                    Release();
                    _ptr = sp._ptr;
                    _pcount = sp._pcount;
                    _mtx = sp._mtx; // 记得拷贝互斥量指针
                    AddCount();
                }
                return *this;
            }
            ~shared_ptr()
            {
                Release();
            }
            T* get()
            {
                cout << "get _ptr: ";
                return _ptr;
            }

            T* operator->()
            {
                return _ptr;
            }

            T& operator*()
            {
                return *_ptr;
            }

            int use_count()
            {
                return *_pcount;
            }
        };
}

用自实现的线程安全的共享指针管理不加锁的对象

正如上述所说,现在的LHR::shared_ptr现在是线程安全的。但是他管理的对象不一定是线程安全的!即这个共享指针我们已经实现的尽可能完善,但是指针指向的对象比如Date d;多线程操作d对象不一定是线程安全的!

namespace LHR
{
    template <class T>
    class shared_ptr
    {
        T* _ptr;
        int* _pcount; // 引用计数的指针
        mutex* _mtx; // 互斥锁的指针
    public:
        shared_ptr(T* ptr)
            : _ptr(ptr)
            , _pcount(new int(1))   // 注意:在构造时申请引用计数的空间,为每一份资源绑定一个引用计数
            , _mtx(new mutex)       // 注意:在构造时申请互斥量的空间,为每一份资源绑定一个互斥量
        {

        }

        void Release()
        {
            bool delete_flag = false;

            //加锁保护引用计数
            _mtx->lock();
            if (--(*_pcount) == 0 && _ptr != nullptr)
            {
                cout << "delete _ptr: " << _ptr << endl;
                delete _ptr;
                delete _pcount; // 记得释放引用计数的空间
                delete_flag = true;
            }
            _mtx->unlock();
            //必须先解锁再销毁互斥锁,因此:
            if (delete_flag)
            {
                delete _mtx; // 记得释放互斥量的空间
            }
        }

        void AddCount()
        {
            //加锁保护引用计数
            _mtx->lock();
            ++(*_pcount);
            _mtx->unlock();
        }

        shared_ptr(const shared_ptr& sp)
            : _ptr(sp._ptr),
            _pcount(sp._pcount),
            _mtx(sp._mtx) // 记得拷贝互斥量指针
        {
            AddCount();
        }

        shared_ptr& operator=(const shared_ptr& sp)
        {
            if (_ptr != sp._ptr)
            {
                Release();
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                _mtx = sp._mtx; // 记得拷贝互斥量指针
                AddCount();
            }
            return *this;
        }
        
        ~shared_ptr()
        {
            Release();
        }
        
        T* get()
        {
            cout << "get _ptr: ";
            return _ptr;
        }

        T* operator->()
        {
            return _ptr;
        }

        T& operator*()
        {
            return *_ptr;
        }

        int use_count()
        {
            return *_pcount;
        }
    };
}

class Date
{
public:
    int _year = 0;
    int _month = 0;
    int _day = 0;
};

void ThreadFunc(LHR::shared_ptr<Date>& sp, int n)
{
    while (n--)
    {
        LHR::shared_ptr<Date> copy(sp);
        //两个线程对同一日期类对象的年月日++100000次,如果是线程安全的,最后的结果应该都是200000。
        ++copy->_year;
        ++copy->_month;
        ++copy->_day;
    }
}

int main()
{
    LHR::shared_ptr<Date> sp(new Date);
    cout << sp.get() << endl;
    int n = 100000;
    thread t1(ThreadFunc, ref(sp), n);
    thread t2(ThreadFunc, ref(sp), n);
    t1.join();
    t2.join();

    cout << "sp.use_count(): " << sp.use_count() << endl;
    //最后打印sp指向的日期类对象的年月日
    cout << sp->_year << "/" << sp->_month << "/" << sp->_day << endl;
    return 0;
}

在这里插入图片描述

用自实现的线程安全的共享指针管理加锁的对象

namespace LHR
{
    template <class T>
    class shared_ptr
    {
        T* _ptr;
        int* _pcount; // 引用计数的指针
        mutex* _mtx; // 互斥锁的指针
    public:
        shared_ptr(T* ptr)
            : _ptr(ptr)
            , _pcount(new int(1))   // 注意:在构造时申请引用计数的空间,为每一份资源绑定一个引用计数
            , _mtx(new mutex)       // 注意:在构造时申请互斥量的空间,为每一份资源绑定一个互斥量
        {

        }

        void Release()
        {
            bool delete_flag = false;

            //加锁保护引用计数
            _mtx->lock();
            if (--(*_pcount) == 0 && _ptr != nullptr)
            {
                cout << "delete _ptr: " << _ptr << endl;
                delete _ptr;
                delete _pcount; // 记得释放引用计数的空间
                delete_flag = true;
            }
            _mtx->unlock();
            //必须先解锁再销毁互斥锁,因此:
            if (delete_flag)
            {
                delete _mtx; // 记得释放互斥量的空间
            }
        }

        void AddCount()
        {
            //加锁保护引用计数
            _mtx->lock();
            ++(*_pcount);
            _mtx->unlock();
        }

        shared_ptr(const shared_ptr& sp)
            : _ptr(sp._ptr),
            _pcount(sp._pcount),
            _mtx(sp._mtx) // 记得拷贝互斥量指针
        {
            AddCount();
        }

        shared_ptr& operator=(const shared_ptr& sp)
        {
            if (_ptr != sp._ptr)
            {
                Release();
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                _mtx = sp._mtx; // 记得拷贝互斥量指针
                AddCount();
            }
            return *this;
        }

        ~shared_ptr()
        {
            Release();
        }

        T* get()
        {
            cout << "get _ptr: ";
            return _ptr;
        }

        T* operator->()
        {
            return _ptr;
        }

        T& operator*()
        {
            return *_ptr;
        }

        int use_count()
        {
            return *_pcount;
        }
    };
}
class Date
{
public:
    int _year = 0;
    int _month = 0;
    int _day = 0;
};

void ThreadFunc(LHR::shared_ptr<Date>& sp, int n, mutex& mtx)
{
    while (n--)
    {
        LHR::shared_ptr<Date> copy(sp);
        //两个线程对同一日期类对象的年月日++100000次,如果是线程安全的,最后的结果应该都是200000。
        lock_guard<mutex> lock(mtx);
        ++copy->_year;
        ++copy->_month;
        ++copy->_day;
    }
}

int main()
{
    LHR::shared_ptr<Date> sp(new Date);
    cout << sp.get() << endl;
    int n = 100000;
    mutex mtx;
    // 线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的
    // 即使线程参数为引用类型,在线程中修改后也不能修改外部实参,
    // 因为其实际引用的是线程栈中的拷贝,而不是外部实参。
    // void ThreadFunc(LHR::shared_ptr<Date>& sPtr)
    // sPtr引用的是sp在线程栈中的拷贝 std::ref()获取真正的引用
    // thread t(ThreadFunc, sp);
    thread t1(ThreadFunc, ref(sp), n, ref(mtx));
    thread t2(ThreadFunc, ref(sp), n, ref(mtx));
    t1.join();
    t2.join();

    cout << "sp.use_count(): " << sp.use_count() << endl;
    //最后打印sp指向的日期类对象的年月日
    cout << sp->_year << "/" << sp->_month << "/" << sp->_day << endl;
    return 0;
}

在这里插入图片描述

C++标准库中的std::shared_ptr要考虑的问题会更多,比如内存碎片、与std::weak_ptr进行配合等。因此std::shared_ptr的具体实现会相当复杂。以上的内容只是对其核心功能的简单模拟,二者的差别其实还是很大的。

weak_ptr非拥有引用 不增加引用计数

C++内存管理:shared_ptr/weak_ptr源码(长文预警) - 知乎 (zhihu.com)

在这里插入图片描述

解决shared_ptr循环引用问题

线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的,因此:即使线程参数为引用类型,在线程中修改后也不能修改外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。

在这里插入图片描述

在这里插入图片描述

  1. node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete。

  2. node1的_next指向node2,node2的_prev指向node1,引用计数变成2。

  3. node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点。

  4. 也就是说_next析构了,node2就释放了。

  5. 也就是说_prev析构了,node1就释放了。

  6. 但是_next属于node1的成员,node1节点释放了,_next才会析构;而node1由_prev管理,_prev属于node2成员,node2节点释放了,_prev才会析构。这就叫循环引用,两者互相牵制谁也不放过谁。

解决循环引用

struct ListNode
{
    std::weak_ptr<ListNode> next;
    std::weak_ptr<ListNode> prev;
    int _val;

    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
};

void Test_shared_ptr2()
{
    std::shared_ptr<ListNode> n1(new ListNode);
    std::shared_ptr<ListNode> n2(new ListNode);

    // 循环引用
    n1->next = n2;
    n2->prev = n1;
}

智能指针shared_ptr满足

符合RAII思想

可以像指针一样使用

拷贝构造和赋值重载控制引用计数

智能指针weaked_ptr满足

不符合RAII思想

可以像指针一样使用

拷贝构造和赋值重载不增加引用计数

没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因

不能使用weak_ptr直接访问对象,而必须调用lock提升为shared_ptr才能访问

不支持内置类型指针构造 支持shared_ptr构造和赋值

辅助解决shared_ptr的循环引用问题

weak_ptr有自己的count

配合expired来判断所指向资源是否还有被指向的必要

即weaked_ptr可以指向资源,但是不参与管理,不增加shared_ptr引用计数

所以存在于一种所指向资源的计数已经成0 此时expired判断是否失效 若所指向资源失效weaked_ptr就不再指向
std::weak_ptr::use_count用于返回引用计数;std::weak_ptr::expired用于检查weak_ptr是否过期,实际上expired的实现就是if(use_count()==0)

模拟实现简易版weak_ptr

#include <iostream>
#include <atomic>

template<typename T>
class SharedPtr;

template<typename T>
class WeakPtr;

// 控制块,用于管理引用计数
struct ControlBlock {
    ControlBlock()
        : shared_count(1)
        , weak_count(0)
    {

    }

    std::atomic<int> shared_count;
    std::atomic<int> weak_count;
};

// SharedPtr 类
template<typename T>
class SharedPtr
{
    // 友元类 WeakPtr 可以访问 SharedPtr 的私有成员
    friend class WeakPtr<T>;
public:
    // 构造函数
    SharedPtr(T* ptr = nullptr)
        : _ptr(ptr)
    {
        if (_ptr)
            _control_block = new ControlBlock();
        else
            _control_block = nullptr;
    }

    // 从“const WeakPtr<T>”转换为“SharedPtr<int>”
    SharedPtr(const WeakPtr<T>& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;

        if (_control_block)
            ++_control_block->shared_count;
    }

    // 拷贝构造函数
    SharedPtr(const SharedPtr& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;

        if (_control_block)
            ++_control_block->shared_count;
    }

    // 移动构造函数
    SharedPtr(SharedPtr&& other) noexcept
        : _ptr(other._ptr)
        , _control_block(other._control_block)
    {
        other._ptr = nullptr;
        other._control_block = nullptr;
    }

    // 赋值重载
    SharedPtr& operator=(const SharedPtr& other)
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;
            if (_control_block)
                ++_control_block->shared_count;
        }
        return *this;
    }

    // 移动赋值
    SharedPtr& operator=(SharedPtr&& other) noexcept
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;

            other._ptr = nullptr;
            other._control_block = nullptr;
        }
        return *this;

    }

    // 析构函数
    ~SharedPtr()
    {
        release();
    }

    // 获取底层指针
    T* get() const
    {
        return _ptr;
    }

    // 获取引用计数
    std::atomic<int> use_count() const
    {
        return _control_block ? _control_block->shared_count.load() : 0;
    }

    // 获取weak_count
    std::atomic<int> weak_count() const
    {
        return _control_block ? _control_block->weak_count.load() : 0;
    }

    // 重载 -> 和 * 操作符
    T* operator->() const
    {
        return _ptr;
    }

    T& operator*() const
    {
        return *_ptr;
    }

    // reset
    void reset(T* ptr = nullptr)
    {
        if (ptr != _ptr)
        {
            release();
            _ptr = ptr;
            if (_ptr)
                _control_block = new ControlBlock();
            else
                _control_block = nullptr;
        }

    }
private:
    T* _ptr;
    ControlBlock* _control_block;

    void release()
    {
        if (_control_block)
        {
            if (--_control_block->shared_count == 0)
            {
                delete _ptr;
                if (_control_block->weak_count == 0)
                    delete _control_block;
            }
        }
    }
};


// WeakPtr 类
template<typename T>
class WeakPtr
{
    // 友元类
    friend class SharedPtr<T>;
public:
    // 默认构造函数
    WeakPtr()
        : _ptr(nullptr)
        , _control_block(nullptr)
    {

    }

    // WeakPtr拷贝构造函数
    WeakPtr(const WeakPtr& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;

        if (_control_block)
            ++_control_block->weak_count;
    }

    // WeakPtr赋值运算符
    WeakPtr& operator=(const WeakPtr& other)
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;

            if (_control_block)
                ++_control_block->weak_count;
        }
        return *this;
    }

    // 由 SharedPtr 构造 WeakPtr
    WeakPtr(const SharedPtr<T>& shared_ptr)
    {
        cout << "WeakPtr(const SharedPtr<T>& shared_ptr)" << endl;

        _ptr = shared_ptr._ptr;
        _control_block = shared_ptr._control_block;

        if (_control_block)
            ++_control_block->weak_count;
    }

    // 由 SharedPtr 赋值给 WeakPtr
    WeakPtr& operator=(const SharedPtr<T>& shared_ptr)
    {
        cout << "WeakPtr& operator=(const SharedPtr<T>& shared_ptr)" << endl;

        if (this != &shared_ptr)
        {
            release();
        }
        _ptr = shared_ptr._ptr;
        _control_block = shared_ptr._control_block;
        if (_control_block)
            ++_control_block->weak_count;

        return *this;
    }

    // 析构函数
    ~WeakPtr()
    {
        release();
    }

    // 实现expired()函数
    bool expired() const
    {
        return !_control_block || _control_block->shared_count == 0;
    }


    // 从 WeakPtr 锁定为 SharedPtr
    SharedPtr<T> lock() const
    {
        if (!expired())
            return SharedPtr<T>(*this);
        else
            return SharedPtr<T>(nullptr);
    }

    // 实现use_count
    int use_count() const
    {
        return _control_block ? _control_block->shared_count.load() : 0;
    }

    // 获取weak_count
    int weak_count() const
    {
        return _control_block ? _control_block->weak_count.load() : 0;
    }

    // reset 方法:重置当前 WeakPtr
    void reset()
    {
        release();

        _ptr = nullptr; // 置空指针
        _control_block = nullptr; // 置空控制块
    }
private:
    T* _ptr;
    ControlBlock* _control_block;

    void release()
    {
        if (_control_block)
        {
            if (--_control_block->weak_count == 0 && _control_block->shared_count == 0)
                delete _control_block;
        }
    }
};

template <typename T>
void WeakPtrShowCount(WeakPtr<T>& wp)
{
    cout << "shared_count: " << wp.use_count() << endl;
    cout << "weak_count: " << wp.weak_count() << endl;
}

template <typename T>
void SharedPtrShowCount(SharedPtr<T>& sp)
{
    cout << "shared_count: " << sp.use_count() << endl;
    cout << "weak_count: " << sp.weak_count() << endl;
}
// 测试代码
int main()
{
    SharedPtr<int> sp(new int(10));
    SharedPtrShowCount(sp);

    WeakPtr<int> wp(sp);
    SharedPtrShowCount(sp);
    WeakPtrShowCount(wp);

    {
        SharedPtr<int> sp2 = wp.lock();
        SharedPtrShowCount(sp);
        WeakPtrShowCount(wp);
        SharedPtrShowCount(sp2);
    }

    return 0;
}

在这里插入图片描述

智能指针应用函数

enable_shared_from_this

enable_shared_from_this 是一个模板类,当一个类从 std::enable_shared_from_this 继承时,它可以使用该模板类的成员函数 shared_from_this() 来获取指向自身的 std::shared_ptr

#include <iostream>
#include <memory>

class MyClass : public std::enable_shared_from_this<MyClass> 
{
public:
    void show() 
    {
        std::cout << "MyClass instance" << std::endl;
    }

    std::shared_ptr<MyClass> getShared()
    {
        return shared_from_this(); // 获取指向自身的 shared_ptr
    }
};

int main() 
{
    std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();
    obj->show();

    std::shared_ptr<MyClass> obj2 = obj->getShared(); // 通过成员函数获取 shared_ptr
    obj2->show();

    return 0;
}

weak_from_this

允许从 enable_shared_from_this 派生的类创建一个 std::weak_ptr。这对于避免循环引用非常重要

#include <iostream>
#include <memory>

class MyClass : public std::enable_shared_from_this<MyClass> {
public:
    void show()
    {
        std::cout << "MyClass instance" << std::endl;
    }

    std::weak_ptr<MyClass> getWeak() 
    {
        return weak_from_this(); // 获取指向自身的 weak_ptr
    }
};

int main() 
{
    std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();
    std::weak_ptr<MyClass> weakObj = obj->getWeak(); // 从 shared_ptr 获取 weak_ptr

    if (auto locked = weakObj.lock()) 
    { 
        // 尝试锁定 weak_ptr
        locked->show();
    } 
    else
    {
        std::cout << "Object has expired." << std::endl;
    }

    obj.reset(); // 释放 shared_ptr

    if (auto locked = weakObj.lock()) 
    {
        locked->show();
    } 
    else 
    {
        std::cout << "Object has expired." << std::endl; // 此时对象已经过期
    }

    return 0;
}

make_shared

make_shared 是一个更高效的方式来创建 std::shared_ptr 实例,它可以减少内存分配次数,提高性能。

#include <iostream>
#include <memory>

class MyClass 
{
public:
    MyClass(int value) 
        : value(value)
    {
            
    }

    void show() 
    {
        std::cout << "Value: " << value << std::endl;
    }

private:
    int value;
};

int main() 
{
    auto obj = std::make_shared<MyClass>(42); // 使用 make_shared 创建共享指针
    obj->show();

    return 0;
}

带删除器的智能指针

补充知识

如何证明sizeof是操作符不是函数

  1. sizeof(a) == sizeof a;sizeof int 报错:sizeof后面必须是一个表达式
  2. sizeof 不涉及函数调用。它在编译时计算大小,不生成任何运行时代码。

new/delete

基础介绍

对于内置类型:

new/delete申请和释放的是单个元素的空间
new[]和delete[]申请和释放的是连续空间

new申请空间失败时抛异常,malloc返回NULL。

自定义类型:

  1. new的原理:

调用operator new函数申请空间
在申请的空间上执行构造函数,完成对象的构造

  1. delete的原理

在空间上执行析构函数,完成对象中资源的清理工作
调用operator delete函数释放对象的空间

  1. new T[N]的原理

调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
在申请的空间上执行N次构造函数

  1. delete[]的原理

在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

深入理解
string *ps = new string[10];// 调用string默认构造初始化    
int *pi = new int[10]; 		// 不初始化
delete [] ps;// n次析构 + 释放空间
delete [] pi;// 释放空间
  1. 自定义类型数组使用new[]时 实际在分配数组空间时多分配了 4 个字节的大小,这4个字节专门保存数组的大小,调用delete [] 时取出这个保存的数x,继而调用析构函数x次。
  2. 实际返回的指针还是对象数组的指针,而不是所有分配空间的起始地址。
  3. delete [] 调用operator delete[]释放空间,operator delete[]中调用x次operator delete来释放空间。
  4. delete [] 调用operator delete[],operator delete[]接收的空间起始地址是p-4,而delete调用operator delete,operator delete接收的空间起始地址是p。故delete无法正确析构new[]出来的自定义类型数组。但是可以正确析构new[]出来的内置类型和 无自定义的析构函数的类 类型

代码场景

~SharedPtr()
{
    if (_control_block)
    {
        if (--_control_block->shared_count == 0)
        {
            delete _ptr;
            if (_control_block->weak_count == 0)
                delete _control_block;
        }
    }
}
void testNODeleter()
{
    struct Date
    {
        int _year = 0;
        int _month = 0;
        int _day = 0;

        // 实现析构函数会报错!
        ~Date()
        {
            cout << "~Date()" << endl;
        }
    };

    SharedPtr<int> sp1(new int[10]);
    SharedPtr<Date> sp2(new Date[10]);
}

当sp2生命周期结束,调用~SharedPtr(),执行delete _ptr;

_ptr指向

在这里插入图片描述

delete _ptr的行为:

  1. 调用Date的析构函数,完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间

实现了Date的析构函数:

调用Date的析构函数时由于Date只有内置类型,自实现的析构函数没有释放Date的空间。以至于只有在delete _ptr调用operator delete时只释放了一个Date!报错!!!!段错误!程序崩溃!

不实现Date的析构函数:

会调用Date的默认析构,默认析构会释放该对象占用的内存。不报错!当前理解:默认析构调用delete[] 成功释放空间。

怎么办?

解决问题的思路:需要调用delete[] 时让他调用deelte[]。通过添加删除器解决!

删除器

std::move()

std::move是C++标准库中的一个函数模板,主要用于实现对象的移动语义。它位于<utility>头文件中,是C++11标准引入的特性。std::move的主要应用场景包括以下几个方面:

1. 触发移动构造函数

当需要将一个对象的状态和资源转移给另一个新创建的对象时,可以使用std::move来触发移动构造函数。这样做可以避免不必要的拷贝操作,提高程序的性能。例如:

std::string str = "Hello, World!";  
std::string str2 = std::move(str); // 触发移动构造函数,将str的内容和资源转移给str2

在这个例子中,std::move(str)str转换为右值引用,从而触发std::string的移动构造函数,将str的内容和资源“移动”到str2中。注意,这里的“移动”实际上是浅拷贝加上原对象状态的废弃,并不是物理上的内存移动。

2. 触发移动赋值运算符

当需要将一个已存在的对象的状态和资源转移给另一个已存在的对象时,可以使用std::move来触发移动赋值运算符。这同样可以避免不必要的拷贝操作,提高程序的效率。例如:

std::string str1 = "Hello";  
std::string str2 = "World";  
str2 = std::move(str1); // 触发移动赋值运算符,将str1的内容和资源转移给str2

在这个例子中,std::move(str1)str1转换为右值引用,从而触发std::string的移动赋值运算符,将str1的内容和资源“移动”到str2中,并释放str1原有的资源。

3. 优化容器操作

在C++标准库中,很多容器类(如std::vectorstd::list等)都支持移动语义。在进行容器元素的插入、删除或交换等操作时,使用std::move可以优化性能。例如,将一个大容器中的元素转移到另一个容器中时,可以使用std::move来避免不必要的元素拷贝:

std::vector<int> vec1 = {1, 2, 3, 4, 5};  
std::vector<int> vec2;  
vec2.reserve(vec1.size()); // 预留足够的空间以避免重新分配  
for (auto& x : vec1) {  
    vec2.push_back(std::move(x)); // 这里的std::move实际上是多余的,因为push_back已经支持移动语义  
}  
// 但如果直接赋值,std::move就很有用了  
vec2 = std::move(vec1); // 将vec1的内容和资源“移动”到vec2中,vec1变为空容器

注意,在这个例子中,对于vec2.push_back(std::move(x)),由于std::vector<int>push_back成员函数已经支持移动语义(如果可用的话),所以这里的std::move实际上是多余的。但是,在直接赋值或进行其他需要显式触发移动语义的操作时,std::move就非常有用了。

4. 返回值优化

在某些情况下,当函数需要返回一个局部对象时,可以使用std::move来避免不必要的拷贝或移动操作。然而,现代C++编译器通常能够自动进行返回值优化(RVO)或命名返回值优化(NRVO),所以在大多数情况下,显式使用std::move可能不是必需的。但是,了解这一点仍然有助于理解std::move在优化返回值方面的潜力。

总结

std::move是C++中用于实现移动语义的重要工具,它通过将对象转换为右值引用来触发移动构造函数或移动赋值运算符的调用,从而避免不必要的拷贝操作并提高程序的性能。其应用场景包括触发移动构造函数、触发移动赋值运算符、优化容器操作以及在某些情况下优化返回值等。然而,需要注意的是,std::move本身并不移动任何数据或资源;它只是改变了对象的值类别并允许编译器选择更高效的移动操作

template<class U, class D>
shared ptr(U* p, D del);

删除器测试函数

template <class T>
struct DeleteArray
{
    void operator()(T* ptr)
    {
       delete[] ptr;
    }
};
void testDeleter()
{
    struct Date
    {
        int _year = 0;
        int _month = 0;
        int _day = 0;

        // 实现析构函数会报错!
        ~Date()
        {
            cout << "~Date()" << endl;
        }
    };
    

    SharedPtr<int> sp0(new int[10]);
    SharedPtr<int> sp1(new int[10], [](int* ptr) { delete[] ptr; });
    //SharedPtr<Date> sp2(new Date[10]); // error
    SharedPtr<Date> sp3(new Date[10], [](Date* ptr) {delete[] ptr; });
    SharedPtr<Date> sp4(new Date[10], DeleteArray<Date>());
    SharedPtr<FILE> sp5(fopen("./main.cpp", "r"), [](FILE* fp) {fclose(fp); });

    auto deleter = [](Date* ptr){ delete[] ptr; };
    SharedPtr<Date> sp(new Date[10], deleter);

}

shared_ptr完整模拟实现

template<typename T>
class SharedPtr;

template<typename T>
class WeakPtr;

// 控制块,用于管理引用计数
struct ControlBlock 
{
    ControlBlock()
        : shared_count(1)
        , weak_count(0)
    {

    }

    std::atomic<int> shared_count;
    std::atomic<int> weak_count;
};

// SharedPtr 类
template<typename T>
class SharedPtr
{
    // 友元类 WeakPtr 可以访问 SharedPtr 的私有成员
    friend class WeakPtr<T>;
private:
    T* _ptr;
    ControlBlock* _control_block;
    std::function<void(T*)> _deleter = [](T* ptr) { delete ptr; };

    void release()
    {
        if (_control_block)
        {
            if (--_control_block->shared_count == 0)
            {
                // delete _ptr;
                _deleter(_ptr);
                if (_control_block->weak_count == 0)
                {
                    delete _control_block;
                    _deleter = std::function<void(T*)>();
                }
            }
        }
    }
   
public:
    // 构造函数
    SharedPtr(T* ptr = nullptr)
        : _ptr(ptr)
    {
        if (_ptr)
            _control_block = new ControlBlock();
        else
            _control_block = nullptr;
    }

    // 带删除器的构造函数
    template<class D>
    SharedPtr(T* ptr, D deleter)
        : _ptr(ptr)
        , _deleter(deleter)
    {
        if (_ptr)
            _control_block = new ControlBlock();
        else
            _control_block = nullptr;
    }

    // 从“const WeakPtr<T>”转换为“SharedPtr<int>”
    SharedPtr(const WeakPtr<T>& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;

        if (_control_block)
            ++_control_block->shared_count;
    }

    // 拷贝构造函数
    SharedPtr(const SharedPtr& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;
        _deleter = other._deleter;

        if (_control_block)
            ++_control_block->shared_count;
    }

    // 移动构造函数
    SharedPtr(SharedPtr&& other) noexcept
        : _ptr(other._ptr)
        , _control_block(other._control_block)
        ,_deleter(std::move(other._deleter))
    {
        other._ptr = nullptr;
        other._control_block = nullptr;
        other._deleter = std::function<void(T*)>();
    }

    // 赋值重载
    SharedPtr& operator=(const SharedPtr& other)
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;
            _deleter = other._deleter;

            if (_control_block)
                ++_control_block->shared_count;
        }
        return *this;
    }

    // 移动赋值
    SharedPtr& operator=(SharedPtr&& other) noexcept
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;
            _deleter = std::move(other._deleter);

            other._ptr = nullptr;
            other._control_block = nullptr;
            other._deleter = std::function<void(T*)>();
        }
        return *this;

    }

    // 析构函数
    ~SharedPtr()
    {
        release();
    }

    // 获取底层指针
    T* get() const
    {
        return _ptr;
    }

    // 获取引用计数
    std::atomic<int> use_count() const
    {
        return _control_block ? _control_block->shared_count.load() : 0;
    }

    // 获取weak_count
    std::atomic<int> weak_count() const
    {
        return _control_block ? _control_block->weak_count.load() : 0;
    }

    // 重载 -> 和 * 操作符
    T* operator->() const
    {
        return _ptr;
    }

    T& operator*() const
    {
        return *_ptr;
    }

    // reset
    void reset(T* ptr = nullptr)
    {
        if (ptr != _ptr)
        {
            release();

            _ptr = ptr;
            if (_ptr)
                _control_block = new ControlBlock();
            else
                _control_block = nullptr;
        }
    }
};

weak_ptr完整模拟实现

template<typename T>
class SharedPtr;

template<typename T>
class WeakPtr;

// WeakPtr 类
template<typename T>
class WeakPtr
{
    // 友元类
    friend class SharedPtr<T>;

private:
    T* _ptr;
    ControlBlock* _control_block;

    void release()
    {
        if (_control_block)
        {
            if (--_control_block->weak_count == 0 && _control_block->shared_count == 0)
                delete _control_block;
        }
    }
public:
    // 默认构造函数
    WeakPtr()
        : _ptr(nullptr)
        , _control_block(nullptr)
    {

    }

    // WeakPtr拷贝构造函数
    WeakPtr(const WeakPtr& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;

        if (_control_block)
            ++_control_block->weak_count;
    }

    // WeakPtr赋值运算符
    WeakPtr& operator=(const WeakPtr& other)
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;

            if (_control_block)
                ++_control_block->weak_count;
        }
        return *this;
    }

    // 由 SharedPtr 构造 WeakPtr
    WeakPtr(const SharedPtr<T>& shared_ptr)
    {
        cout << "WeakPtr(const SharedPtr<T>& shared_ptr)" << endl;

        _ptr = shared_ptr._ptr;
        _control_block = shared_ptr._control_block;

        if (_control_block)
            ++_control_block->weak_count;
    }

    // 由 SharedPtr 赋值给 WeakPtr
    WeakPtr& operator=(const SharedPtr<T>& shared_ptr)
    {
        cout << "WeakPtr& operator=(const SharedPtr<T>& shared_ptr)" << endl;

        if (this != &shared_ptr)
        {
            release();
        }
        _ptr = shared_ptr._ptr;
        _control_block = shared_ptr._control_block;
        if (_control_block)
            ++_control_block->weak_count;

        return *this;
    }

    // 析构函数
    ~WeakPtr()
    {
        release();
    }

    // 实现expired()函数
    bool expired() const
    {
        return !_control_block || _control_block->shared_count == 0;
    }


    // 从 WeakPtr 锁定为 SharedPtr
    SharedPtr<T> lock() const
    {
        if (!expired())
            return SharedPtr<T>(*this);
        else
            return SharedPtr<T>(nullptr);
    }

    // 实现use_count
    int use_count() const
    {
        return _control_block ? _control_block->shared_count.load() : 0;
    }

    // 获取weak_count
    int weak_count() const
    {
        return _control_block ? _control_block->weak_count.load() : 0;
    }

    // reset 方法:重置当前 WeakPtr
    void reset()
    {
        release();

        _ptr = nullptr; // 置空指针
        _control_block = nullptr; // 置空控制块
    }
};

智能指针完整代码

using namespace std;

#include <iostream>
#include <atomic>

template<typename T>
class SharedPtr;

template<typename T>
class WeakPtr;

// 控制块,用于管理引用计数
struct ControlBlock 
{
    ControlBlock()
        : shared_count(1)
        , weak_count(0)
    {

    }

    std::atomic<int> shared_count;
    std::atomic<int> weak_count;
};

// SharedPtr 类
template<typename T>
class SharedPtr
{
    // 友元类 WeakPtr 可以访问 SharedPtr 的私有成员
    friend class WeakPtr<T>;


private:
    T* _ptr;
    ControlBlock* _control_block;
    std::function<void(T*)> _deleter = [](T* ptr) { delete ptr; };

    void release()
    {
        if (_control_block)
        {
            if (--_control_block->shared_count == 0)
            {
                // delete _ptr;
                _deleter(_ptr);
                if (_control_block->weak_count == 0)
                {
                    delete _control_block;
                    _deleter = std::function<void(T*)>();
                }
            }
        }
    }
   
public:
    // 构造函数
    SharedPtr(T* ptr = nullptr)
        : _ptr(ptr)
    {
        if (_ptr)
            _control_block = new ControlBlock();
        else
            _control_block = nullptr;
    }

    // 带删除器的构造函数
    template<class D>
    SharedPtr(T* ptr, D deleter)
        : _ptr(ptr)
        , _deleter(deleter)
    {
        if (_ptr)
            _control_block = new ControlBlock();
        else
            _control_block = nullptr;
    }

    // 从“const WeakPtr<T>”转换为“SharedPtr<int>”
    SharedPtr(const WeakPtr<T>& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;

        if (_control_block)
            ++_control_block->shared_count;
    }

    // 拷贝构造函数
    SharedPtr(const SharedPtr& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;
        _deleter = other._deleter;

        if (_control_block)
            ++_control_block->shared_count;
    }

    // 移动构造函数
    SharedPtr(SharedPtr&& other) noexcept
        : _ptr(other._ptr)
        , _control_block(other._control_block)
        ,_deleter(std::move(other._deleter))
    {
        other._ptr = nullptr;
        other._control_block = nullptr;
        other._deleter = std::function<void(T*)>();
    }

    // 赋值重载
    SharedPtr& operator=(const SharedPtr& other)
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;
            _deleter = other._deleter;

            if (_control_block)
                ++_control_block->shared_count;
        }
        return *this;
    }

    // 移动赋值
    SharedPtr& operator=(SharedPtr&& other) noexcept
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;
            _deleter = std::move(other._deleter);

            other._ptr = nullptr;
            other._control_block = nullptr;
            other._deleter = std::function<void(T*)>();
        }
        return *this;

    }

    // 析构函数
    ~SharedPtr()
    {
        release();
    }

    // 获取底层指针
    T* get() const
    {
        return _ptr;
    }

    // 获取引用计数
    std::atomic<int> use_count() const
    {
        return _control_block ? _control_block->shared_count.load() : 0;
    }

    // 获取weak_count
    std::atomic<int> weak_count() const
    {
        return _control_block ? _control_block->weak_count.load() : 0;
    }

    // 重载 -> 和 * 操作符
    T* operator->() const
    {
        return _ptr;
    }

    T& operator*() const
    {
        return *_ptr;
    }

    // reset
    void reset(T* ptr = nullptr)
    {
        if (ptr != _ptr)
        {
            release();

            _ptr = ptr;
            if (_ptr)
                _control_block = new ControlBlock();
            else
                _control_block = nullptr;
        }
    }

};


// WeakPtr 类
template<typename T>
class WeakPtr
{
    // 友元类
    friend class SharedPtr<T>;

private:
    T* _ptr;
    ControlBlock* _control_block;

    void release()
    {
        if (_control_block)
        {
            if (--_control_block->weak_count == 0 && _control_block->shared_count == 0)
                delete _control_block;
        }
    }
public:
    // 默认构造函数
    WeakPtr()
        : _ptr(nullptr)
        , _control_block(nullptr)
    {

    }

    // WeakPtr拷贝构造函数
    WeakPtr(const WeakPtr& other)
    {
        _ptr = other._ptr;
        _control_block = other._control_block;

        if (_control_block)
            ++_control_block->weak_count;
    }

    // WeakPtr赋值运算符
    WeakPtr& operator=(const WeakPtr& other)
    {
        if (this != &other)
        {
            release();

            _ptr = other._ptr;
            _control_block = other._control_block;

            if (_control_block)
                ++_control_block->weak_count;
        }
        return *this;
    }

    // 由 SharedPtr 构造 WeakPtr
    WeakPtr(const SharedPtr<T>& shared_ptr)
    {
        cout << "WeakPtr(const SharedPtr<T>& shared_ptr)" << endl;

        _ptr = shared_ptr._ptr;
        _control_block = shared_ptr._control_block;

        if (_control_block)
            ++_control_block->weak_count;
    }

    // 由 SharedPtr 赋值给 WeakPtr
    WeakPtr& operator=(const SharedPtr<T>& shared_ptr)
    {
        cout << "WeakPtr& operator=(const SharedPtr<T>& shared_ptr)" << endl;

        if (this != &shared_ptr)
        {
            release();
        }
        _ptr = shared_ptr._ptr;
        _control_block = shared_ptr._control_block;
        if (_control_block)
            ++_control_block->weak_count;

        return *this;
    }

    // 析构函数
    ~WeakPtr()
    {
        release();
    }

    // 实现expired()函数
    bool expired() const
    {
        return !_control_block || _control_block->shared_count == 0;
    }


    // 从 WeakPtr 锁定为 SharedPtr
    SharedPtr<T> lock() const
    {
        if (!expired())
            return SharedPtr<T>(*this);
        else
            return SharedPtr<T>(nullptr);
    }

    // 实现use_count
    int use_count() const
    {
        return _control_block ? _control_block->shared_count.load() : 0;
    }

    // 获取weak_count
    int weak_count() const
    {
        return _control_block ? _control_block->weak_count.load() : 0;
    }

    // reset 方法:重置当前 WeakPtr
    void reset()
    {
        release();

        _ptr = nullptr; // 置空指针
        _control_block = nullptr; // 置空控制块
    }
};

template <typename T>
void WeakPtrShowCount(WeakPtr<T>& wp)
{
    cout << "shared_count: " << wp.use_count() << endl;
    cout << "weak_count: " << wp.weak_count() << endl;
}

template <typename T>
void SharedPtrShowCount(SharedPtr<T>& sp)
{
    cout << "shared_count: " << sp.use_count() << endl;
    cout << "weak_count: " << sp.weak_count() << endl;
}

void testMYSmartPointer()
{
    SharedPtr<int> sp(new int(10));
    SharedPtrShowCount(sp);

    WeakPtr<int> wp(sp);
    SharedPtrShowCount(sp);
    WeakPtrShowCount(wp);

    {
        SharedPtr<int> sp2 = wp.lock();
        SharedPtrShowCount(sp);
        WeakPtrShowCount(wp);
        SharedPtrShowCount(sp2);
    }
}
template <class T>
struct DeleteArray
{
    void operator()(T* ptr)
    {
       delete[] ptr;
    }
};
void testDeleter()
{
    struct Date
    {
        int _year = 0;
        int _month = 0;
        int _day = 0;

        // 实现析构函数会报错!
        ~Date()
        {
            cout << "~Date()" << endl;
        }
    };
    

    SharedPtr<int> sp0(new int[10]);
    SharedPtr<int> sp1(new int[10], [](int* ptr) { delete[] ptr; });
    //SharedPtr<Date> sp2(new Date[10]); // error
    SharedPtr<Date> sp3(new Date[10], [](Date* ptr) {delete[] ptr; });
    SharedPtr<Date> sp4(new Date[10], DeleteArray<Date>());
    SharedPtr<FILE> sp5(fopen("./main.cpp", "r"), [](FILE* fp) {fclose(fp); });

    auto deleter = [](Date* ptr){ delete[] ptr; };
    SharedPtr<Date> sp(new Date[10], deleter);

}

// 测试代码
int main()
{
    //testMYSmartPointer();

    testDeleter();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿猿收手吧!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值