智能指针——C++

这篇具有很好参考价值的文章主要介绍了智能指针——C++。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

智能指针相较于普通指针的区别,就是智能指针可以不用主动释放内存空间,系统会自动释放,避免了内存泄漏。

1、unique_ptr:独占指针

需包含的头文件:#include <memory>

unique_ptr 三种定义方式

先定义一个类

class cat
{
private:
	std::string name{"mimi"};
publiccat(std::string name);
	~cat();
	void cat_info() const //输出一下成员name的值
	{
		std::cout<<"name:"<<this->name<<std::endl;
	}
	void set_cat_name(const std::string &name) //修改成员name的值
	{
	this->name=name;
	}
	std::string get_name() const
	{
		return name;
	}
};
  1. 使用std::make_unique的方式:推荐使用的方式
	//方法一 std::make_unique
	std::unique_ptr<cat> u_c_p4 = std::make_unique<cat>("ee");
	std::string a=u_c_p4->get_name();
	std::cout << "a:" << a << std::endl;

程序结束自动调用析构函数
智能指针——C++

  1. 使用new的方式声明智能指针
	//方法二 new
	std::unique_ptr<cat> u_c_p3{ new cat("cc")};

	std::cout << "-------------" << std::endl;

智能指针——C++
我们只new了这个类对象,并没有delete释放他的地址,但是智能指针会在程序执行结束自动调用这个类的析构函数。

  1. 利用普通指针声明
	cat *c_p2 = new cat("xy"); //声明一个普通指针
	std::unique_ptr<cat> u_c_p2{ c_p2 }; // 声明一个智能指针指向普通指针的地址
	
	//正能指针和普通指针的指向的地址相同,两个指针任意一个改变,对方也会跟着改变
	c_p2->cat_info();
	u_c_p2->cat_info();
	c_p2->set_cat_name("aa");
	c_p2->cat_info();
	u_c_p2->cat_info();
	u_c_p2->set_cat_name("bb");
	c_p2->cat_info();
	u_c_p2->cat_info();
	std::cout << "-------------" << std::endl;
	return 0;

智能指针——C++

用该方法声明智能指针有一个确定,普通指针依然可以使用,如果普通指针改变地址的值,那么智能指针的值也会跟着改变。

unique_ptr 做函数参数

  • unique_ptr不能被复制,只可以move,即当要让unique_ptr 的变量做函数参数,直接将变量放在括号里是不可以的,可以用move函数
void do_unique_value(std::unique_ptr<cat> c)
{
	c->cat_info();
}
int main()
{
	//方法一 std::make_unique
	std::unique_ptr<cat> u_c_p4 = std::make_unique<cat>("ee");
	do_unique_value(u_c_p4);   //直接赋值到括号里是不行的
	u_c_p4->cat_info(); // 在move之前是可以调用的,但是move之后是不能调用的
	do_unique_value(std::move(u_c_p4));  //要用move函数进行值传递
	
	u_c_p4->cat_info(); //这里就能调用了
	//但是在move之后这个变量是不能调用的

	do_unique_value(std::make_unique<cat>()); // 直接构造

	std::cout << "-------------" << std::endl;
	return 0;
}

智能指针——C++

如果值是引用的话,可以不用move

void do_unique_value2(std::unique_ptr<cat> &c)
{
	c->cat_info();
}
	do_unique_value(u_c_p4);  //参数是引用,就可以直接传值
	u_c_p4->cat_info();  //并且之后也可以在使用该变量

2、shared_ptr 计数指针、共享指针

shared_ptr基础概念

  • shared_ptr区别于unique_ptr的区别是shared_ptr可以被复制,shared_ptr创建了一个计数器对象,与类对象所指向的内存地址关联。
  • copy一次shared_ptr的计数器就加1,销毁一次计数器就减一
  • 调用shared_ptr计数器的API是use_count()
  1. shared_ptr指针变量的赋值后,use_count()计数值就为1,然后将shared_ptr 变量赋给其他值,本身和另一个值的value值相同,计数值都为2,是在原本shared_ptr值为1的基础上+1。
  2. 如果两个以上存储着相同地址的shared_ptr值,其中一个给赋值为nullptr,的话,那么只有被赋值为空的变量值为空,其余几个value值不变,但是计数值都会在原有基础上-1。
	std::shared_ptr<int> i_p_1 = std::make_shared<int>(10);
	std::cout << "value:" << *i_p_1 << std::endl;
	// 第一次赋值计数为1
	std::cout << "use count:" << i_p_1.use_count() << std::endl; 
	std::cout << "-------------" << std::endl;
	//结果
	// value:10
	// use count : 1
	
	//copy
	std::shared_ptr<int> i_p_2 = i_p_1;
	std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	//将i_p_1的值给i_p_2,对该地址有一次进行复制操作,计数+1,i_p_1和i_p_2代表的地址相同,存储值和计数值都相同
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	//结果:
	//value:10
    //value:10
    //use count : 2
    //use count : 2

	//修改值
	*i_p_2 = 30;
	std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	//结果
	//i_p_1 value : 30
	//i_p_2 value : 30
	//i_p_1 use count : 2
	//i_p_2 use count : 2
	
	// 三个值表示同一个地址,将其中一个赋值为nullptr
	std::shared_ptr<int> i_p_3 = i_p_1;
	std::cout << "---------i_p_1 = nullptr befo---------" << std::endl;
	std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	std::cout << "i_p_3 value:" << *i_p_3 << std::endl;
	// 将i_p_1赋值给i_p_3,计数值在2的基础上+1=3
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_3.use_count() << std::endl;
	i_p_1 = nullptr;
	std::cout << "------i_p_1 = nullptr after-----------"<<std::endl;
	//std::cout << "i_p_1 value:" << *i_p_1 << std::endl; //赋空之后不能输出
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	std::cout << "i_p_3 value:" << *i_p_3 << std::endl;
	//i_p_1赋值为空后,i_p_1的计数值直接为0,但是i_p_2和i_p_3的计数值会在原来3的基础上-1
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_3.use_count() << std::endl;
	// 结果
	// ---------i_p_1 = nullptr befo---------
	//i_p_1 value : 30
	//i_p_2 value : 30
	//i_p_3 value : 30
	//i_p_1 use count : 3
	//i_p_2 use count : 3
	//i_p_2 use count : 3
	//------i_p_1 = nullptr after-----------
	//i_p_2 value : 30
	//i_p_3 value : 30
	//i_p_1 use count : 0
	//i_p_2 use count : 2
	//i_p_2 use count : 2

shared_ptr函数

shared_ptr变量是可以copy,可以被当做值直接复制给函数,传入函数体后,在函数体内部计数值依然会进行加减操作,但操作尽在函数体内有效,出了函数体,计数值会恢复到原来的数值。但是在函数体里面对指针的值进行修改的话,该值会被修改。

void cat_by_value(std::shared_ptr<cat> cat)
{
	cat->set_cat_name("ee");
	cat->cat_info();
	std::cout << "use count:" << cat.use_count() << std::endl;
}

	std::shared_ptr<cat> c1 = std::make_shared<cat>("dd");
	c1->cat_info();
	std::cout << "c1 use count:" << c1.use_count() << std::endl;
	std::cout << "-------------" << std::endl;

	cat_by_value(c1);
	std::cout << "-------------" << std::endl;

	c1->cat_info();
	std::cout << "c1 use count:" << c1.use_count() << std::endl;
	//结果
	//cat:dd
	//name:dd
	//c1 use count : 1
	//------------ -
	//name : ee
	//use count : 2
	//------------ -
	//name : ee
	//c1 use count : 1
	//del : ee

3、shared_ptr与unique_ptr

  • 不能将shared_ptr转化为unique_ptr
  • 可以将unique_ptr转化为shared_ptr,使用std::move

如果函数有返回值的话,返回值最好设计为unique_ptr,的类型,应为unique_ptr既可以赋值给shared_ptr,也可以赋值给shared_ptr

  • 可以用move函数将unique_ptr的值赋给shared_ptr类型
  • 也可以将函数返回的unique_ptr类型的数据直接赋值给shared_ptr类型
std::unique_ptr<cat> get_unique_ptr()
{
	std::unique_ptr<cat> cat_p = std::make_unique<cat>("local cat");
	return cat_p;
}

int main()
{
	std::unique_ptr<cat> c_p_1 = std::make_unique<cat>("dd");
	// 使用move的方式赋值
	std::shared_ptr<cat> c_p_2 = std::move(c_p_1);
	std::cout << "use count:" << c_p_2.use_count() << std::endl;
	c_p_2->get_name();
	std::cout << "use count:" << c_p_2.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	// 函数返回值赋值
	std::shared_ptr<cat> c_p_3 = get_unique_ptr();
	c_p_3->get_name();
	std::cout << "use count:" << c_p_3.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	
	return 0;
}

weak_ptr

  • weak_ptr没有内存的所有权,所以不能调用->和解引用*
  • weak_ptr可以通过lock()提升为shared_ptr类型

weak_ptr不能单独存在,一般需要借助shared_ptr,来声明

  • 将shared_ptr的值给w_p_1对象,是可以的,并且w_p_1可以调用use_count()计数值,但是在飞w_p_1对象赋值的过程中shared_ptr这个对象的计数值不会+1
  • 使用lock(),将weak_ptr的值赋值给shared_ptr,两者的use_count()计数值都会相加。
	std::shared_ptr<cat> s_p_1 = std::make_shared<cat>("c1");
	//将shared_ptr的值给w_p_1对象,是可以的,并且w_p_1可以调用use_count()计数值,但是在飞w_p_1对象赋值的过程中shared_ptr这个对象的计数值不会+1
	std::weak_ptr<cat> w_p_1(s_p_1);

	std::cout << "s_p_1 count:" << s_p_1.use_count() << std::endl;
	std::cout << "w_p_1 count:" << w_p_1.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	std::shared_ptr<cat> s_p_2 = w_p_1.lock();
	std::cout << "s_p_1 count:" << s_p_1.use_count() << std::endl;
	std::cout << "w_p_1 count:" << w_p_1.use_count() << std::endl;
	std::cout << "s_p_1 count:" << s_p_2.use_count() << std::endl;
//cat:c1  //构造函数
//s_p_1 count:1
//w_p_1 count:1
//-------------
//s_p_1 count:2
//w_p_1 count:2
//s_p_1 count:2
//del:c1  //析构函数

循环依赖的问题文章来源地址https://www.toymoban.com/news/detail-421700.html

class cat
{
public:
	cat();
	cat(std::string name);
	~cat();
	void set_frient(std::shared_ptr<cat> c)
	{
		m_friend = c;
	}
private:
	std::string name{ "mimi" };
	std::shared_ptr<cat> m_friend;  
};
	std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
	std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
	//运行结果
	//cat:c3		//构造
	//cat:c4 	//构造
	//del:c4		//析构
	//del:c3		//析构

	如果在c3和c4的类中都定义一个cat类型的变量存储着对方,那么两者就存在着依赖
	std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
	std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
	c3->set_frient(c4);
	c4->set_frient(c3);
	//运行结果,只够构造、没有析构
	// cat:c3
	// cat:c4
解决方式只需将类定义时的shared_ptr类型的对象改为weak_ptr
class cat
{
public:
	cat();
	cat(std::string name);
	~cat();
	void set_frient(std::shared_ptr<cat> c)
	{
		m_friend = c;
	}
private:
	std::string name{ "mimi" };
	std::weak_ptr<cat> m_friend; //这需要把这里修改为weak_ptr类型
};
	// 然后运行相同的代码
	std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
	std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
	c3->set_frient(c4);
	c4->set_frient(c3);
 //运行结果
 //cat:c3
 //cat:c4
 //del:c4
 //del:c3

到了这里,关于智能指针——C++的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】智能指针详解

    今天我们来讲一下c++中的智能指针。 智能指针不是指针,是一个管理指针的类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。 动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源 C++ 98 中产

    2024年02月04日
    浏览(42)
  • 【C++学习】智能指针

    🐱作者:一只大喵咪1201 🐱专栏:《C++学习》 🔥格言: 你只管努力,剩下的交给时间! 如上图代码所示,在Func中开辟动态空间,在调用完Division函数后释放该空间。 如果Division没有抛异常,那么动态空间会被正常释放。 如果Division抛了异常,就会去匹配对应的catch,而Fu

    2024年02月06日
    浏览(41)
  • 智能指针——C++

    智能指针相较于普通指针的区别,就是智能指针可以不用主动释放内存空间,系统会自动释放,避免了内存泄漏。 需包含的头文件: #include memory unique_ptr 三种定义方式 先定义一个类 使用std::make_unique的方式:推荐使用的方式 程序结束自动调用析构函数 使用new的方式声明智能

    2023年04月22日
    浏览(34)
  • C++——智能指针

    内存泄漏 什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对 该段内存的控制,因而造成了内存的浪费。 内存泄漏的危害:长期运行的

    2024年02月09日
    浏览(58)
  • 面试:C++ 11 智能指针

    内存泄露在维基百科中的解释如下: 在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了

    2024年02月07日
    浏览(27)
  • C++新特性:智能指针

    智能指针主要解决以下问题: 1)内存泄漏:内存手动释放,使用智能指针可以自动释放 2)共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题,例如同样的数据帧,但是业务A和业务B处理的逻辑不一样(都是只读)。可以用 shared_ptr 共享数据帧对象的所有

    2024年02月09日
    浏览(29)
  • c++ 学习系列 -- 智能指针

    C++ 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。但使用普通指针,容易造成内存泄露(忘记释放)、二次释放、程序发生异常时内存泄露等问题等。 另外,使用普通指针容易产生 野指针、悬空指针 等问题。 所以 C++11 就引入了智能指

    2024年02月13日
    浏览(40)
  • C++进阶 智能指针

    本篇博客简介:介绍C++中的智能指针 我们首先来看下面的这段代码 在上面这段代码中有着一个很明显的内存泄露风险 当我们的程序运行在Func函数内的div函数时 很可能因为除0错误而跳转到另外一个执行流从而导致Func函数内两个new出来的内存没法被回收 为了解决这个问题我

    2024年02月13日
    浏览(35)
  • C++智能指针的发展

    GC–garbage collection垃圾回收,Java里的机制。在头文件 memory 中 堆内存泄漏(Heap leak) 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那

    2023年04月19日
    浏览(23)
  • 【C++入门到精通】智能指针 [ C++入门 ]

    在C++编程中,内存管理一直是一个重要的话题。手动分配和释放内存可能会导致各种问题,例如内存泄漏和悬挂指针,这些问题往往会导致程序崩溃或产生不可预测的结果。为了解决这些问题, C++提供了一种称为智能指针的机制,它可以自动管理内存分配和释放,从而避免了

    2024年01月21日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包