首页 > 编程语言 >C++智能指针

C++智能指针

时间:2022-10-01 23:23:00浏览次数:47  
标签:std include cout C++ 智能 unique ptr 指针

C++智能指针

需要头文件 <memory>

不需要手动释放指针

不是所有指针都能封装成智能指针,很多时候原始指针更加方便。

std::unique_ptr

  • 任何时刻都只能有一个指针管理内存
  • 当指针超出作用域时自动释放
  • 该类型指针不能copy只能move。即转移指针的所有权,记得将原指针置空。使用新new出的类时默认使用move方法。

创建方式:

  1. 通过裸指针创建(创建后将原始指针置空并销毁,否则不能满足独占的要求)
  2. 通过new创建
  3. 通过 std::make_unique 创建(推荐)

通过 get() 获取地址,通过 -> 调用成员函数,通过*解引用

#include <memory>
#include <iostream>
#include <string>

int main()
{
	MyClass* p = new MyClass("abc");
	std::unique_ptr<MyClass> pp{ p };

	std::cout << pp->get_name() << std::endl;
	pp->set_name("123");
	std::cout << pp->get_name() << std::endl;

	std::cout << "--------------- end of main --------------" << std::endl;
}

函数调用传参

  1. passing by value
    使用 std::move 获取内存所有权
    如果参数传入 make_unique<>() 语句自动转为 move
  2. passing by reference
    如果设置参数为 const 则不能改变指向
    比如智能指针清空方法 reset()
  3. return by value
    指向一个局部变量
    可用作链式函数
#include <memory>
#include <iostream>
#include <string>

void pass_by_value(std::unique_ptr<MyClass> c)
{
	std::cout << c->get_name() << std::endl;
	std::cout << "------ end of function `pass_by_value` ------" << std::endl;
}

void pass_by_reference(std::unique_ptr<MyClass> &c)
{
	c.reset(); // 重置指针,将指针设置为 0
}

void pass_by_const_reference(const std::unique_ptr<MyClass> &c)
{
	c->set_name("new name"); // 可以修改对象内部的属性
	//c.reset(); 不能使用该方法
}

// 尽量不要使用这种方法
std::unique_ptr<MyClass> get_unique_ptr()
{
	// p 对象在函数结束后销毁,p中的原始指针所有权转移到函数调用者手中
	std::unique_ptr<MyClass> p = std::make_unique<MyClass>("local unique_ptr");
	std::cout << "p.get(): " << p.get() << std::endl; // 01100000 堆上
	std::cout << "&p: " << &p << std::endl;           // 00EFF5AC 栈上
	return p;
}

int main()
{
	// 1. pass by value
	std::unique_ptr<MyClass> p1 = std::make_unique<MyClass>("MyClass p1");
	//pass_by_value(p1); // 这种清空会报错
	pass_by_value(std::move(p1)); // 使用move函数后,所有权转到调用函数中,函数结束时销毁对象
	p1 = nullptr;

	std::cout << std::endl;

	// 2. pass by reference (经常用)
	std::unique_ptr<MyClass> p2 = std::make_unique<MyClass>("MyClass p2");
	pass_by_const_reference(p2);
	pass_by_reference(p2);
	std::cout << "unique_ptr after reset passed by reference: " << p2.get() << std::endl;

	std::cout << std::endl;

	// 3. return unique_ptr, chain calls
	get_unique_ptr()->get_name();


	std::cout << "--------------- end of main --------------" << std::endl;
}
/*
MyClass p1                                                                        
------ end of function `pass_by_value` ------
Destruct of : MyClass p1

Destruct of : new name
unique_ptr after reset passed by reference: 00000000

p.get(): 007C0318
&p: 006FFC44
Destruct of : local unique_ptr
--------------- end of main --------------    
*/

std::shared_ptr

计数指针 共享指针,可以共享数据。

创建了一个计数器与类对象所指内存关联,copy则计数器加一,销毁则计数器减一。api 为use_count()

  1. pass by value
    copy 方式,计数器加一
  2. pass by reference
    const 表示不可以改变智能指针的指向
  3. return by value(没什么用,尽量返回 unique_ptr)
    链式调用
#include <memory>
#include <iostream>
#include <string>

void pass_by_value(std::shared_ptr<MyClass> c)
{
	std::cout << "use_count : " << c.use_count() << std::endl;
}

void pass_by_ref(std::shared_ptr<MyClass> &c)
{
	std::cout << "address of shared_ptr : " << &c << std::endl;
	// 使用 const& 后无法使用 reset
	c.reset(new MyClass("new class"));
	std::cout << "use_count : " << c.use_count() << std::endl; // 引用传值不会增加计数器的数量
}


int main()
{
	// 1. pass by value
	std::shared_ptr<MyClass> p = std::make_shared<MyClass>("123");
	//函数内 use_count 为 2, 函数外 use_count 为 1
	pass_by_value(p);
	std::cout << "use count in main function : " << p.use_count() << std::endl;

	std::cout << std::endl;

	// 2. pass by reference
	std::cout << "address of shared_ptr : " << &p << std::endl;
	std::cout << "MyClass's address before reset : " << p.get() << std::endl;
	pass_by_ref(p);
	std::cout << "MyClass's address after reset : " << p.get() << std::endl;
	std::cout << "name of p after reset : " << p->get_name() << std::endl;

	std::cout << std::endl;

	std::cout << "--------------- end of main --------------" << std::endl;
	return 0;
}

/*
use_count : 2
use count in main function : 1

address of shared_ptr : 00AFFD78
MyClass's address before reset : 00E3679C
address of shared_ptr : 00AFFD78
Destruct of : 123
use_count : 1
MyClass's address after reset : 00E41280
name of p after reset : new class

--------------- end of main --------------
Destruct of : new class
*/

unique_ptr 和 shared_ptr

unique_ptr 可以转为 shared_ptr(通过 std::move()),但是 shared_ptr 不能转为 unique_ptr

返回值尽量使用 unique_ptr 指针

#include <memory>
#include <iostream>
#include <string>

std::unique_ptr<MyClass> get_unique_ptr()
{
	return std::make_unique<MyClass>("new MyClass");
}

int main()
{
	std::unique_ptr<MyClass> p1 = std::make_unique<MyClass>("123");
	std::shared_ptr<MyClass> p2 = std::move(p1); // p1内部的对象已经转移到了p2中
	std::cout << "use_count of p2 : " << p2.use_count() << std::endl;
	std::cout << "address of p1.get() : " << p1.get() << std::endl;
	//p1->get_name(); // 此语句报错

	std::unique_ptr<MyClass> p3 = get_unique_ptr();
	std::shared_ptr<MyClass> p4 = get_unique_ptr();
	std::cout << "p3'name : " << p3->get_name() << std::endl;
	std::cout << "p4'name : " << p4->get_name() << std::endl;

	std::cout << "--------------- end of main --------------" << std::endl;
	return 0;
}

/*
use_count of p2 : 1
address of p1.get() : 00000000

p3'name : new MyClass
p4'name : new MyClass
--------------- end of main --------------
Destruct of : new MyClass
Destruct of : new MyClass
Destruct of : 123
*/

std::weak_ptr

不拥有所有权,不能调用 -> 和 *

使用场景:A类中需要存储其他A类型对象的信息,如果使用 shared_ptr 就会出现循环依赖问题.所以这里使用没有所有权的指针标记该对象

class MyClass
{
public:
	MyClass() = default;
	MyClass(const std::string);
	~MyClass();
	std::string get_name() const;
	void set_name(const std::string);
	void set_friend(const std::shared_ptr<MyClass>);
private:
	std::string name;
	std::weak_ptr<MyClass> myFriend; // 此处如果使用 shared_ptr 在函数结束自动销毁时可能无法正常销毁关联类,从而造成内存泄露
};

可以使用 lock() 函数升级成 shared_ptr 类型指针

#include <memory>
#include <iostream>
#include <string>

int main()
{
	std::shared_ptr<MyClass> p1 = std::make_shared<MyClass>("123");
	std::weak_ptr<MyClass> p2{ p1 };
	std::cout << "p1'use_count : " << p1.use_count() << std::endl;
	std::cout << "p2'use_count : " << p2.use_count() << std::endl;

	std::cout << std::endl;

	std::shared_ptr<MyClass> p3 = p2.lock();
	std::cout << "p1'use_count : " << p1.use_count() << std::endl;
	std::cout << "p2'use_count : " << p2.use_count() << std::endl;
	std::cout << "p3'use_count : " << p3.use_count() << std::endl;

	std::cout << "--------------- end of main --------------" << std::endl;
	return 0;
}

标签:std,include,cout,C++,智能,unique,ptr,指针
From: https://www.cnblogs.com/zhh567/p/16745671.html

相关文章

  • C++ 编程中常用的英文单词(首字母是A、B、C开头)
    学习编程不一定需要英语水平很高,能记住认识一些常用的英文单词也可以,有看不明白的文档资料也可以使用翻译工具,编写代码时大部分好用的IDE都是有代码提示的。本文主要介绍C+......
  • C++ 编程中常用的英文单词(首字母是D、E、F开头)
    学习编程不一定需要英语水平很高,能记住认识一些常用的英文单词也可以,有看不明白的文档资料也可以使用翻译工具,编写代码时大部分好用的IDE都是有代码提示的。本文主要介绍C+......
  • 对c++的一些思考
    能用初始化列表就用能写explicit就写能用c++11自带的跨平台函数、对象就用、但也要分情况,如果想完全知道自己在干什么就用系统API能自己写的就不要让编译器......
  • C++ 彩票器
    #include<iostream>#include<ctime>#include<cstdlib>usingnamespacestd;intmain(intargc,char**argv){//1.获取一下你买的彩票号码intnumber=0;......
  • C语言常见关键字+指针
    c语言的常见关键字有:auto,break,case,char,const(常变量),continue,default(默认),do,double,else,enum(枚举),extern(引入外部符号),float,for,goto,if,int,long,register(寄存器关......
  • 贤鱼的刷题日常-【c++】P7909 [CSP-J 2021] 分糖果
    ✅创作者:贤鱼⏰预计时间:15分钟@​​TOC​​题目题目背景红太阳幼儿园的小朋友们开始分糖果啦!题目描述红太阳幼儿园有n个小朋友,你是其中之一。保证n≥2。有一天你在幼儿园......
  • 如何区分指针常量和常量指针
    一、概念常量指针:常量指针本质是指针,常量修饰它,表示这个指针是一个指向常量的指针(变量)。指针指向的对象是常量,那么这个对象不能被更改在C/C++中,常量指针是像这样声明的:1......
  • 从特斯拉人形机器人亮相看AI人工智能模型落地面临的两个难题
    当地时间9月30日,美国特斯拉公司的人形机器人-“擎天柱”(Optimus)亮相,这款机器人之前在一些媒体的报道中,有些功能已经展现了不俗的表现,令人叹为观止。​在这次的亮相中,又展现......
  • C++ 第46课八卦图读取
    #include<bits/stdc++.h>#include"minecraft.h"usingnamespacestd;TxMinecraftmc;stringmeiju(stringX,stringZ){ stringx,z; intid=251,data=15,cis......
  • 智能化改造和数字化转型哪家企业做的好?
    没有人会告诉你他做的不好,你得到的答复肯定是他们都是专业的!但他的成功案例搬到你这儿很可能就是失败的!适合自己的才是最有可能取得成功的要想更好的进行数字化转型最重要......