c++智能指针

c++智能指针

本文来源于课程: https://www.bilibili.com/video/BV1LY411H7Gg?share_source=copy_web&vd_source=96e137d103dfc2464739bfd0533bc61a

(因为这一部分的内容对我来说很容易忘,特此整理)


智能指针可以在程序结束时自动释放,这一特性就像调用了构造函数和析构函数

unique_ptr

和普通的指针区别在它会在函数结束时自动释放自己的生命,而普通指针需要程序员记住delete它,忘记delete就会造成内存泄露问题

只需要:

std::unique_ptr<int> one = std::make_unique<int>();

这里的make_unique<T>()相当于调用了创建unique指针的函数(所以不能用{}


unique指针不能作为参数传进其他函数,因为它删除了拷贝构造函数:禁止拷贝(如果不删,就可能会出现两次释放同一指针的问题)

要是想传递它,下面有几个解决方案:

  1. get下指针,只是暂时借用了指针的数据结构,不掌握指针的生命

    class test{
    public:
        test(){
            std::cout << "这里是一个unique指针!" << std::endl;
        }
        ~test(){
            std::cout << "I\'m die——" << std::endl;
        }
        void showSelf(){
            std::cout << "我被借用一下~" << std::endl;
        }
    };
    void show(test *p){
        p->showSelf();
    }
    int main(){
        std::unique_ptr<test> p = std::make_unique<test>();
        show(p.get());
        return 0;
    }
    
  2. 移动指针的生命到另一个函数里,原函数中的指针被设置为空

    vector<unique_ptr> ptrlist{};	// 设置一个全局变量
    void func(std::unique_ptr<test> p){
        ptrlist.push_back(std::move(p)); 	// 把p进一步放到ptrlist里
    }
    int main(){
    	std::unique_ptr<test> p = std::make_unique<test>();
        return 0;
    }
    

    其中的std::move()只是一个声明,可以意味类型转化:unique指针需要接受的是右值引用,没有std::move也可以转化

    但是遇上vector这种,所有元素的拷贝都是深拷贝(除了智能指针有例外),会把变量拷贝到一个新的内存中(包括数据、包括结构),性能不高;当智能指针指向一个没有拷贝构造函数的类(Test::test(const test& one)=delete;)时,拷贝就为浅拷贝(仅拷贝指针)

移动的好处是浅拷贝,不需要再次拷贝结构,速度更快;但我们还想使用移动前的指针,除了在移动指针前先test another = p.get(),还可以考虑shared_ptr

注意,test another = p.get()还要考虑移动后的指针生命;也就是说get是借用着那个移动指针的生命,在移动指针生命结束时、another也会寄

shared_ptr

shared指针则支持拷贝,像名字一样、可以多个指针指向同一对象;它内置了一个计数器,通过当前共有几个shared指针存在,判断是否释放。只要有一个指针还指着该对象,对象就不会被释放

void func(std::shared_ptr<test> other);

std::shared_ptr<test> one = std::make_shared<test>(); //初始化引用计数为1
func(one); 		// 引用计数+1,函数退出引用计数-1
func(one);		// 引用计数又+1

可以使用one.use_count()获取one指针当前的引用计数

这里one.use_count()调用了指针本身的方法,也就是.运算符可以访问自己类中的方法;而one->show()是调用了one指向的、对象的方法,也就是指针要用->访问所指对象的类方法

看起来shared_ptr基本无敌了,但它仍有不足

(1)会出现循环引用

struct example{
    std::shared_ptr<example> myparent;
    std::shared_ptr<example> mychild;
};
int main(){
	auto parent = std::make_shared<example>();
    auto child = std::makde_shared<example>();
    parent->mychild = child; 	//parent指着example对象,让该对象的mychild指向child
    child->myparent = parent;	//child指着example对象,让该对象的myparent指向parent
    
    return 0;
}

我们其实只是想使用example类,并用指针方便调类方法,但是上面的例子就出现了child和parent两个指针相互指着,引用计数不为0,结果它们都无法释放最后内存泄漏(可以用不影响计数的普通指针、weak_ptr来避免)

使用普通指针:

struct test{
    std::shared_ptr<test> myparent;
    test *mychild;			// 使用普通指针
};

parent->mychild = child;
child->myparent = parent.get();	// 获取普通指针

使用weak_ptr:

struct test{
    std::shared_ptr<test> myparent;
    std::weak_ptr<test> mychild;
};

parent->mychild = child;	// 指向弱引用
child->myparent = parent;

(2)shared_ptr需要维护一个atomic的引用计数器,额外管理一块内存空间效率较低;访问实际对象需要二级指针,且deleter使用了类型擦除技术

weak_ptr

弱引用weak_ptr不影响shared_ptr的引用计数

  • 如果有需要可以随时使用p.lock()建立强引用,不用lock()就不影响计数
  • shared_ptr失效时(计数为0),weak_ptr的lock()将返回nullptr
std::shared_ptr<test> p = std::make_shared<test>();
std::weak_ptr<test> weakp = p;
weakp.lock()->show();		//建立强引用

在类中的智能指针应用

  • unique:某一对象仅属于我,比如父子窗口,父窗口指向子窗口时(浏览器关掉一个标签页)
  • 原始指针:当某一对象不仅仅属于我,但他被释放前我必然需要先被释放,比如子窗口指向父窗口时(浏览器关闭,诸多标签页需要先一步释放)
  • shared:“我”被多个对象共享 || 某对象仅属于“我”,但需要weak_ptr
  • weak:该对象不属于我,且他被释放时我可能还需要活着,指向窗口中上一次点击的链接(比如浏览器里,从一个标签页中打开了另一个标签页)
posted @ 2022-09-16 15:57  比萨在哭  阅读(26)  评论(0编辑  收藏  举报