C++ 智能指针

本文介绍了C++智能指针,它是管理动态内存的机制,可解决内存泄漏和空悬指针问题。C++中有std::shared_ptr、std::unique_ptr和std::weak_ptr三种类型。还详细阐述了它们的创建方法、相关函数(如reset、move、use_count、lock等)的用法,以及可能出现的死循环问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

智能指针概述

是什么:

智能指针是C++中用来管理动态内存的一种机制。它通过对指针对象进行封装,使得在特定的作49·47用域内自动管理内存的分配和释放。

解决的问题:

智能指针的主要用途是解决C++中常见的内存泄漏和空悬指针问题。它提供了一种方便而安全地使用动态分配对象的方法。

分类:

在C++中,有三种智能指针类型:std::shared_ptr、std::unique_ptr和std::weak_ptr。

std::shared_ptr:是一种共享所有权的智能指针。它可以多个指针同时拥有同一个对象,对象只有在最后一个拥有者释放时才会被销毁。这种指针可以通过std::make_shared函数来创建,也可以使用new关键字来创建。

std::unique_ptr:是一种独占所有权的智能指针。只能有一个unique_ptr指向某个对象。当unique_ptr销毁时,其所指向的对象也会被自动删除。这种指针可以使用new关键字来创建,也可以通过std::make_unique函数来创建

std::weak_ptr:不拥有对象的所有权,只是对shared_ptr的一个引用。它可以用来检测对象是否已被删除,并避免循环引用。

智能指针用法:

新建一个Pointers类来举例说明,类定义如下:

#include<memory>

class Pointers {
	public:
		Pointers():name("First"){};
        Pointers(std::string name):name(name){
	        std::cout << "Pointers is constructor:" <<name<< std::endl;
        }
        ~Pointers() {
	        std::cout << "Pointers is destroy:" <<name<< std::endl;
        }
		void info()const {
			std::cout << "Pointers name:" << name<<std::endl;
		}
		std::string getName()const {
			return name;
		}
		void setName(const std::string& name) {
			this->name = name;
		}
		void setSName(std::shared_ptr<Pointers> c) {
			w_ptr_p= c;
		}

private:
	std::string name;   //	std::string name = { "First" };
	std::weak_ptr<Pointers> w_ptr_p;
};

创建方法(有三种,不包括weak_ptr):


	/*智能指针第一种创建方法*/
	Pointers* p = new Pointers("A");
	std::unique_ptr<Pointers>u_c_p{ p };
	p = nullptr;




	/*智能指针第二种创建方法*/
	std::unique_ptr<Pointers>u_c_p1(new Pointers("B"));   //隐式创建类


	/*智能指针第三中创建方法*/
	std::unique_ptr<Pointers>u_c_p2 = std::make_unique<Pointers>("C");


    来自文心一言:
    关于第一种方法,虽然它在技术上可行,但通常不推荐这样做,因为它首先使用new手动分配了内存,然后将其传递给std::unique_ptr。如果在这两个步骤之间发生异常,那么分配的内存就会泄漏。使用std::make_unique或直接在std::unique_ptr的构造函数中调用new(如第二种方法所示)可以避免这个问题,因为这两个操作都是原子的,即要么都成功,要么都不执行。

reset()函数:

该方法用于释放智能指针所指向的资源。

        (1)std::unique_ptr 调用reset()时:这个函数用于释放 std::unique_ptr 当前管理的对象(如果有的话),并将 std::unique_ptr 重置为不拥有任何对象的状态,即将内部指针设置为 nullptrreset() 函数可以接受一个可选的参数,该参数是一个指向新对象的指针。如果提供了这个参数,则 reset() 会先释放当前管理的对象(如果有的话),然后接管对新对象的所有权。如果未提供参数,则只释放当前管理的对象。

        (2)当用std::shared_ptr调用 reset 方法时:智能指针会将其内部的计数器减一,如果计数器减至零,则会自动释放其所指向的资源。

unique_ptr指针用法:

move()函数:
        std::unique_ptr不能复制,只能move:

      这个函数用于将 unique_ptr 的所有权转移给另一个 unique_ptr。当一个 unique_ptr 调用 move() 函数后,它就会变成一个空指针,而所有权则转移给了另一个 unique_ptr

void uniq_ptr_pass_value(std::unique_ptr<Pointers> ptr) {
	ptr->setName("value");
	std::cout << "通过move传递,原始智能指针失效:" << ptr->getName() << std::endl;
}

int maint(){
	std::unique_ptr<Pointers> pt = std::make_unique<Pointers>("pt");
	uniq_ptr_pass_value(move(pt));

    uniq_ptr_pass_value(std::make_unique<Pointers>("pt1"));        //相当于调用了move


    pt->->info();   //报错,  通过move传递后,pt变成空指针
}



       通过move(),将unique_ptr转换为shared_ptr :
	unique_ptr<Pointers> c_p = make_unique<Pointers>("yy");
	shared_ptr<Pointers>s_p = move(c_p);
unique_ptr与函数:

        因为unique_ptr不能复制, 所以除了move(),还可以引用的方式作为函数参数。此时需要注意有const和没有const的情况。

void uniq_ptr_ref(std::unique_ptr<Pointers>& ptr) {
	ptr.reset();        //没有加const时可以调用reset();
}
void uniq_ptr_refconst(const std::unique_ptr<Pointers>& ptr) {
	ptr.reset();        //有const限定符时会报错,
}

int main(){
    
    std::unique_ptr<Pointers> c_p_1 =std::make_unique<Pointers>("aa");
    uniq_ptr_ref(c_p_1);        //此函数会释放c_p_1指向的资源并把指针c_p_1置为空
    c_p_1->info();              //报错,c_p_1为空。
    
    std::unique_ptr<Pointers> c_p_2 =std::make_unique<Pointers>("bb");
    uniq_ptr_refconst(c_p_2);
    c_p_2->info();              //正常输出
}

shared_ptr指针用法:

use_count()函数:

    该函数返回指向由 shared_ptr 管理的对象的共享所有权的引用计数。

	shared_ptr<Pointers> c_p_1 = make_shared<Pointers>("D");
	shared_ptr<Pointers> c_p_2 = c_p_1;
	shared_ptr<Pointers> c_p_3{ c_p_2 };

	cout << "c_p_1 use_count: " << c_p_1.use_count() << endl;    //输出3
	cout << "c_p_2 use_count: " << c_p_2.use_count() << endl;    //输出3
    cout << "c_p_3 use_count: " << c_p_3.use_count() << endl;    //输出3
shared_ptr与函数:
void shared_ptr_value(shared_ptr<Pointers> ptr) {
	cout << "func use_count: " << ptr.use_count() << endl;    
}
void shared_ptr_ref(shared_ptr<Pointers>& ptr) {
	cout << "func use_count: " << ptr.use_count() << endl;
}
void shared_ptr_ref_const(const shared_ptr<Pointers>& ptr) {
	cout << "func use_count: " << ptr.use_count() << endl;
    //ptr.reset();         //这里不能被reset,因为有const限制
}

int main(){
       std::shared_ptr<Pointers> c_p_1=std::make_shared<Pointers>("cc");

        shared_ptr_value(c_p_1);                       //输出2
        std::cout<<c_p_1.use_count()<<std::endl;        //输出1
        
        shared_ptr_ref(c_p_1);                          //输出1
        shared_ptr_ref_const(c_p_1);                    //输出1


}

weak_ptr:

        weak_ptr创建

        weak_ptr指针不能直接创建,通过指向shared_ptr得到weak_ptr指针。

	shared_ptr<Pointers>s_ptr_p1 = make_shared<Pointers>("ff");
	weak_ptr<Pointers>w_ptr_p1{ s_ptr_p1 };    //得到一个weak_ptr指针

    std::cout<<s_ptr_p1.use_count()<<std::endl;        //输出1,weak_ptr不会增加shared_ptr的计数


    
      lock()函数

        lock() 函数的主要用途是尝试获取对对象的访问权。如果 std::weak_ptr 所指向的对象仍然存在,那么 lock() 函数将返回一个指向该对象的 std::shared_ptr,并且该 std::shared_ptr 的引用计数加一。如果对象不存在,那么 lock() 函数将返回一个空的 std::shared_ptr

	shared_ptr<Pointers>s_ptr_p = make_shared<Pointers>("hh");    
    
    shared_ptr<Pointers>s_ptr_p1(new Pointers("gg"));

    weak_ptr<Pointers>w_ptr_p = s_ptr_p;                           //weak_ptr指向s_ptr_p

    

	s_ptr_p1 = w_ptr_p.lock();                    //如果w_ptr_p指向的对象存在,lock()函数返回有效的std::shared_ptr,并让s_ptr_p1的引用计数器加1,如果不存在就返回空的std::shared_ptr
    if(s_ptr_p1){
        // 对象仍然存在,可以安全地使用它  
            std::cout << s_ptr_p1.use_count()<<std::endl;            //输出2
    } else {  
            // 对象已经被删除,不能使用它  
            std::cout << "sptr2 points to an invalid object\n";  
    }                            
shared_ptr死循环问题:

        在使用 std::shared_ptr 时没有正确地管理共享所有权,可能会导致死循环问题。死循环问题通常发生在两个或多个 shared_ptr 对象相互引用时,导致它们之间的引用计数永远不会减少到零,从而导致程序无法正常结束。

#include<iostream>
#include<memory>

class Pointers {
	public:
		Pointers()=default;
        Pointers(std::string name):name(name){
	        std::cout << "Pointers is constructor:" <<name<< std::endl;
        }
        ~Pointers() {
	        std::cout << "Pointers is destroy:" <<name<< std::endl;
        }

		void setName(std::shared_ptr<Pointers> c) {
			m_sptr= c;
            //m_wptr=c;
		}

private:
	std::string name{"First"};   //	std::string name = { "First" };
	//std::weak_ptr<Pointers> m_wptr;
    std::shared_ptr<Pointers> m_sptr;
};


int main(){

        std::shared_ptr<Pointers> s_ptr_p= std::make_shared<Pointers>("jj");
        std::shared_ptr<Pointers> s_ptr_p1= std::make_shared<Pointers>("kk");
        //创建了两个shared_ptr<Pointers>

        s_ptr_p->setName(s_ptr_p1);        
        s_ptr_p1->setName(s_ptr_p);
        //两个shared_ptr对象相互引用,  此时程序会进入死循环,永远不能释放这两个指针


        //此时,可用weak_ptr指针来避免死循环。把Pointers类的成员变量-shared_ptr换成weak_ptr就可以了
        

}





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值