智能指针 | 叹红颜,近晚霞
Cobb 678 0

operator()
operator*()
operator->()

注意点:

  1. 实现相应运算符重载函数的时候,最好按照运算符本身的含义来实现,不要实现反义或者歧义的逻辑
  2. 不是所有的运算符都可以重载的 比如 . sizeof 是无法重载的
  3. string字符串类 函数对象=》lambda表达式结合算法来使用 智能指针

问题一:

智能指针:防止内存(资源泄漏) 原理:利用栈上的对象,出函数作用域自动调用析构函数的特点,在析构函数里面 调用delete释放资源

问题二:

能不能在堆上定义智能指针 SmartPtr *ptr = new SmartPtr(new int(50)); 肯定不能,因为智能指针对象无法析构,导致new int资源无法释放

问题三:

智能指针为什么表现的和裸指针一样 operator*() operator->() 才让对象表现的和指针一样,称作“智能指针”

两类智能指针: 不带引用计数的: 一个资源只能有一个智能指针指向 带引用计数的:一个资源能有 多个智能指针指向

问题四:auto_ptr(在C++17被干掉了)

1、C++库里面的auto_ptr能够使用吗? 不建议使用,因为 auto_ptr对象在做拷贝构造或者赋值时,直接做资源转移(所有权转移),原先的 auto_ptr对象底层裸指针被置成NULL了,再访问就会出错 因此,如果要使用,需开发者保证不涉及拷贝构造和赋值操作

int main()
{
    auto_ptr<int> ptr1(new int(50));
    *ptr1 = 500;
    cout << *ptr1 << endl;

    auto_ptr<int> ptr2(ptr1);

    *ptr1 = 500;  // 访问空指针了!!!
    cout << *ptr1 << endl;
}

问题五:

你都知道哪些智能指针呢? auto_ptr 是C++11之前的 C++11里面提供了更多好用的智能指针 unique_ptr

shared_ptr weak_ptr
*/

问题六:

你在代码上怎么解决资源泄漏问题,或者你代码上现在出现内存泄漏,你怎么办? //int *p:裸指针

小技巧:智能指针好用的方法

小技巧:
int main()
{
int* p = new int;
unique_ptr<int> ptr(p);   // 把裸指针传给智能指针,出作用域就可自动释放了
*p = 10;    // 继续操作就行
}

SmartPtr

#include <iostream>
#include <string>      // strlen strcpy strcmp strcat  #include<string.h>
#include <functional>  // 包含了C++库提供的函数对象
#include <algorithm>   // 包含了C++库提供的算法  sort 快排
#include <memory>      // 包含了C++库所有的智能指针
#include <stdlib.h>
#include <time.h>
#include <vector>
using namespace std;  // using 这是指示符         using声明只声明一个 std::vector



class SmartPtr
{
public:
    SmartPtr(int* ptr = nullptr)
    {
        this->ptr_ = ptr;
    }
    ~SmartPtr()
    {
        delete this->ptr_;
    }
    // 指针解引用运算符的重载函数
    int& operator*() { return *ptr_;  }
    // 指向符运算符的重载函数
    int* operator->() { return ptr_; }
private:
    int* ptr_;
};


int main()
{
    // int* p1 = new int;  *p1 = 50;
    // ptr1:栈
    SmartPtr ptr1(new int(50));
    // ptr1.operator*() => *ptr_ = 100
    *ptr1 = 100;
    cout << *ptr1 << endl;

    SmartPtr ptr2(ptr1);

    /*
    忘记写delete释放堆内存;写了delete,但是代码逻辑没有运行到delete
    最终导致内存泄漏!!!
    python、golang、php、java   《=  垃圾回收器GC        stop  the  world
    int* p1 = new int;

    if (xxx)
        return;

    delete p1;
    */
}

unique_ptr


//int* GetDataMemory()
//{
//    int* p = new int(100);
//    return p;
//}
unique_ptr<int> GetDataMemory()
{
    unique_ptr<int> p(new int(100));
    return p;
}
int main()
{
    unique_ptr<int> p1(new int(10));

    /*unique_ptr并没有出现像auto_ptr那样的所有权转移操作,导致之前的智能指针底层全部
    成空指针了

      它是通过把unique_ptr的拷贝构造和operator=直接给=delete掉了
    */
    //unique_ptr<int> p2(p1);
    //unique_ptr<int> p2;
    //p2 = p1;

    /*
    但是unique_ptr提供了右值引用参数的拷贝构造和operator=函数,也是做资源转移。
    */
    //int* p = GetDataMemory();
    //*p = 100;

    unique_ptr<int> p = GetDataMemory();
    *p = 100;

}

不带引用计数的智能指针(一个资源只能由一个智能指针指向) auto_ptr unique_ptr 带引用计数的智能指针:

  1. 智能指针引用资源,给资源的引用计数 +1
  2. 智能指针出作用域析构,先给资源的引用计数 -1,引用计数为0,再释放资源

weak_ptr

weak_ptr:弱智能指针(无法改变资源的引用计数) 相当于一个观察者 weak_ptr通过查看资源的引用计数,来判断资源是否还存在,或者已经被释放掉了 weak_ptr因为没有存储一个指针指向资源,所以不能直接访问资源,但是可以通过lock提升方法 把一个weak_ptr =》 shared_ptr,如果转换成功,可以通过shared_ptr来访问资源

注意: 创建对象的时候用shared_ptr,但是其它引用对象的地方全部使用weak_ptr,如果后面想 访问对象的成员,可以把weak_ptr通过lock提升成shared_ptr,达到访问资源方法的目的

make_shared

make_shared() 比直接使用shared_ptr更安全! 想想两块资源:如果资源new成功,引用计数内存new失败??? make_shared是把资源的内存和引用计数的内存一次性new出来放在一起的 不会出现内存泄露问题(资源的内存成功 但引用计数的内存申请失败的情况)

循环引用问题1

带引用计数的智能指针 shared_ptr:强智能指针(可以改变资源的引用计数) shared_ptr的交叉引用(循环引用)问题吗? 导致智能指针(资源计数无法清零)指向的资源无法释放/对象无法调用析构函数(A,B)



// 循环引用
#if 0
struct B; // 前置声明
struct A
{
    A() { cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void afunc() { cout << "A的方法" << endl; }

    weak_ptr<B> ptrb_;
    // shared_ptr<B> ptra_;  // 循环引用
};

struct B
{
    B() { cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }
    void bfunc()
    {
        // 想访问一下A对象的afunc方法,怎么访问???
        // 通过观察资源的引用计数是否为0,来判断是否提升成功
        shared_ptr<A> sp = ptra_.lock(); 
        if (sp != nullptr)
        {
            sp->afunc();
        }
        else
        {
            cout << "B想访问A对象的afunc方法,但是weak_ptr提升失败了" << endl;
        }
    }
    weak_ptr<A> ptra_;
    // shared_ptr<A> ptra_;  // 循环引用
};

int main()
{
    shared_ptr<A> p1(new A());
    shared_ptr<B> p2(new B());

    p1->ptrb_ = p2;
    p2->ptra_ = p1;

    p1.~shared_ptr();

    p2->bfunc();
}

#endif


循环引用问题2

#include <iostream>
#include <memory>    // 包含C++库智能指针
#include <string>
#include <typeinfo>
using namespace std;


struct B; // 前置声明
struct A
{
    A() { cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void afunc() { cout << "A的方法" << endl; }

    weak_ptr<B> ptrb_;
};
struct B
{
    B() { cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }
    void bfunc()
    {
        // 想访问一下A对象的afunc方法,怎么访问???
        // 通过观察资源的引用计数是否为0,来判断是否提升成功
        shared_ptr<A> sp = ptra_.lock(); 
        if (sp != nullptr) // sp不为空,表示提升成功了;sp为空,表示提升失败了
        {
            sp->afunc();
        }
        else
        {
            cout << "B想访问A对象的afunc方法,但是weak_ptr提升失败了" << endl;
        }
    }
    weak_ptr<A> ptra_;
};

int main()
{
    //shared_ptr<A> p1(new A());
    //shared_ptr<B> p2(new B());
    shared_ptr<A> p1 = make_shared<A>(); // new A()
    shared_ptr<B> p2 = make_shared<B>(); // new B()

    p1->ptrb_ = p2;
    p2->ptra_ = p1;

    //p1.~shared_ptr();

    p2->bfunc();
}

share_ptr很强的一个问题

shared_ptr 在只能指针里面封存一个基类类型的指针,需要注意点什么?

1、理解题目 2、缩小题目范围:根据shared_ptr p1(new int[2]); p1底层的裸指针是int类型 那么shared_ptr ,底层的裸指针是Base类型 问题就变成了delete 基类指针需要注意的问题? 3、解释: 基类的析构函数前要virtual。 就是父类指针指向子类对象的释放呀。 父类析构函数前 如果没加virtual,则只会执行父类的 析构函数,如果加了 virtual,父类就会通过虚函数表 和虚函数表指针 找到子类的析构函数 则先执行子类的析构函数从而释放掉子类对象,再执行父类的析构函数

评论区

索引目录