C++智能指针

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;
}
posted @ 2022-10-01 23:10  某某人8265  阅读(43)  评论(0编辑  收藏  举报