C++智能指针及简单实现

C++智能指针

  • 堆内存、栈内存与静态内存
    • 静态内存
    • 栈内存
    • 堆内存
  • 动态内存管理
    • new、delete运算符
    • 智能指针
    • 实现智能指针
  • shared_ptr
    • 智能指针的线程安全问题
    • 解决
  • unique_ptr
  • weak_ptr
    • 循环引用
  • 思维导图
  • 本模块思路

动态内存管理 - cppreference.com

堆内存、栈内存与静态内存

静态内存

  • **保存局部static对象。**例如统计函数本身被调用了多少次,可以在函数体内定义一个static对象,不会随函数体结束而销毁,更特别的是,只会在第一次使用时初始化。
  • 保存类的static成员。类的static成员仅与类有关,而非与类的每个对象关联,更重要的是,static成员的更新会应用到类的每个对象。
  • 全局变量

栈内存

  • 函数内的非静态对象

堆内存

  • 保存动态分配的对象

动态内存管理

new、delete运算符

  • new 在申请内存的同时,还会调用对象的构造函数,返回指向该对象的指针
  • delete 在释放内存之前,会调用对象的析构函数
  • 易产生问题:内存泄漏(忘记释放)、引用非法内存指针(释放早了)

智能指针

位于memory头文件,智能指针是模板类(类似vector),所以相当于把指针包成一个类,添加一些成员(use_count等)的同时使得指针拥有了构造函数和析构函数,这样我们只需要关注内存的申请,内存的释放则由程序自动完成。

下面先手动实现一个智能指针:

实现智能指针

smartptr.h

#pragma once
#ifndef SMART_PTR_H
#define SMART_PTR_H
#include

template<typename T>
class SmartPtr
{
public:
    //默认构造函数
    SmartPtr() :ptr(nullptr), count(nullptr) {}
    //传指针构造函数
    SmartPtr(T* _ptr) : ptr(_ptr), count(nullptr) 
    {
        if (_ptr)count = new int(1);
    }
    //拷贝构造函数
    SmartPtr(const SmartPtr& smp)
    {
        ptr = smp.ptr;
        count = smp.count;
        if (count)(*count)++;
    }
    //析构函数
    ~SmartPtr()
    {
        reset();
    }
    //重载=运算符
    SmartPtr& operator=(const SmartPtr& smp)
    {
        if (this == &smp)//指向同一块共享内存
        {
            return *this;
        }

        reset();//不一块内存,递减count
        this->ptr = smp.ptr;
        this->count = smp.count;
        if (count)(*count)++;

        return *this;
    }
    //重载*运算符
    T operator*() {
        return *(this->ptr);
    }
    //重载->运算符
    T* operator->() {
        return this->ptr;
    }
    //取出原始指针
    T* get()
    {
        return this->ptr;
    }
    //检查是否只有一个共享指针
    bool unique()
    {
        return *count == 1;
    }
    //返回计数器
    int use_count()
    {
        return *count;
    }
    //析构时削减共享计数并检查
    void reset()
    {
        if (count)
        {
            (*count)--;
            if (*count == 0)
            {
                delete this->ptr;
                delete this->count;
            }
        }
    }

private:
    T* ptr;
    int* count;
};

#endif // !SMART_PTR_H

main.cpp

#include
#include
#include"smartptr.h"

using std::make_shared;
using std::shared_ptr;
using std::cout;
using std::endl;

int main()
{
    //对比int *sp = 100;
    auto sp = make_shared<int>(100);
    auto mysp = SmartPtr<int>(new int(100));
    cout << "shared_ptr:" << *sp << endl;
    cout << "My shared_ptr:" << *mysp << endl;
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;
    auto sp2 = shared_ptr<int>(sp);
    auto mysp2 = SmartPtr<int>(mysp);
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;

    auto p = sp.get();
    auto myp = mysp.get();

    auto sp3 = make_shared<int>();
    auto mysp3 = SmartPtr<int>();
    sp3 = sp2;
    mysp3 = mysp2;
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;

    auto sp4 = make_shared<int>(10);
    auto mysp4 = SmartPtr<int>(new int(10));
    mysp3 = mysp4;
    sp3 = sp4;
    cout << "shared_ptr use_count:" << sp.use_count() << endl;
    cout << "My shared_ptr use_count:" << mysp.use_count() << endl;
    cout << "shared_ptr unqiue:" << sp.unique() << endl;
    cout << "My shared_ptr unique:" << mysp.unique() << endl;

    getchar();
    return 0;
}

shared_ptr

在这里插入图片描述
智能指针会将new和delete的过程自动化,它本质上是一个原始指针的包装。
一个允许多个对象指向同一块内存的指针对象,会对该内存地址的引用数量进行计数,shared ptr中除了有一个指针,指向所管理数据的地址。还有一个指针指向一个控制块的地址,里面存放了所管理数据的数量 (常说的引用计数) 、weak ptr的数量、删除器、分配器等,这里控制块是线程安全的,但是所管理数据的地址不是,
C++智能指针及简单实现_第1张图片

智能指针的线程安全问题

多个线程同时修改同一个shared_ptr对象,线程不安全

两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,原因可看i++是原子操作吗,引用计数原来是1,++了两次,可能还是2.这样引用计数就错乱了,具体可以看C++ 智能指针线程安全的问题中的例子。

所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何context switch (切换到另一个线程)。 通常所说的原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。

解决

加锁,但是更好的方法是尽量不要多线程地改变指针指向

也可以用原子智能指针:C++ 20 引入了原子智能指针std::atomic

unique_ptr

unique_ptr 不共享它所管理的对象,两个unique_ptr不能指向同一个对象,unique”占有“它的指针,不能赋值和拷贝,智能释放指针或是对控制权进行转移。

使用时,先include
在这里插入图片描述
这样我们就定义了一个名为entity的智能指针,一个更好的出于防止构造函数抛出异常的定义是:
在这里插入图片描述

weak_ptr

使用weak_ptr复制shared_ptr时不会增加引用计数,这是它最大的特点
这种指针不具有指针功能,最大作用在于解决循环引用的问题

循环引用

可以理解为形成了循环链表,A要释放就要先释放指向A的B,B要释放就要先释放指向B的A,这种时候会造成内存泄漏,此时将其中一个改成weak_ptr,然后在需要获取操作权的时候使用weak_ptr.lock()返回指向共享内存空间的shared_ptr()即可,详见c++ weak ptr解除指针循环引用

思维导图

C++智能指针及简单实现_第2张图片

本模块思路

你可能感兴趣的:(C++,c++,学习,笔记)