C++智能指针
#include<memory>// 智能指针定义在此头文件中智能指针是C++标准库提供的、用于封装原始指针的类模板。其核心优势在于能够自动管理动态分配的内存,通过确定性的析构来防止内存泄漏,从而简化资源管理并提升代码安全性。
1. 独占指针:unique_ptr
- 在任何给定的时刻,只能有一个
unique_ptr指针拥有并管理其指向的内存。 - 当
unique_ptr超出其作用域时,它所管理的内存将自动被释放。 unique_ptr不支持拷贝(copy),但支持移动(move)语义以转移所有权。
1.1 创建 unique_ptr
- 通过已有裸指针创建
Cat *p1 = new Cat(); std::unique_ptr<Cat> u_p1{p1};- 通过new创建
std::unique_ptr<Cat> u_p2 {new Cat()};- 通过std::make_unique创建
std::unique_ptr<Cat> u_p3 = make_unique<Cat>();unique_ptr 可以通过 get() 获取原始指针
std::unique_ptr<Cat> u_p3 = make_unique<Cat>(); std::cout << "address: " << u_p3.get() << std::endl;unique_ptr 实现了 -> 与 * 运算符
- 可以通过
->运算符调用成员函数。 - 可以通过
*运算符解引用。
1.2 unique_ptr与函数调用
- unique_ptr 不可 copy,只能 move
- 在做函数参数或是返回值中一定要注意所有权
值传递
需要用std::move来转移内存拥有权
如果参数直接传入std::make_unique语句,会自动转换为std::move
#include <memory> #include <iostream> using namespace std; void fun1(std::unique_ptr<Cat> p){ count << "address" << p->get() << endl; } int main() { std::unique_ptr<Cat> p1 = make_unique<Cat>(); fun1(std::make_unique<Cat>()); // 自动转换为 move fun1(std::move(p1)); return 0; }调用函数后不能再使用p1,因为p1的所有权已经转移给形参p。在fun1执行完毕后,p离开作用域,其管理的对象会被自动析构。
引用传递
如果设置参数为const则不能改变指向
eg:reset() //清空智能指针
#include <memory> #include <iostream> using namespace std; void fun2(std::unique_ptr<Cat> &p) { count << "address" << p->get() << endl; p.reset(); // 清空 p,再打印地址会指向 0 } void fun3(const std::unique_ptr<Cat> &p) { count << "address" << p->get() << endl; // p.reset(); 修改 p 的指向会崩溃 } int main() { std::unique_ptr<Cat> p2 = make_unique<Cat>(); std::unique_ptr<Cat> p3 = make_unique<Cat>(); fun2(p2); fun3(p3); return 0; }unique_ptr作为引用传参时,所有权并未转移,因此不需要使用std::move。
在fun2中,由于参数是非常量引用,可以修改指针的指向,例如调用reset()来释放其管理的对象。
在fun3中,因为参数是const引用,可以访问其管理的对象(例如*p),但不能修改指针本身的指向(例如调用reset()),否则会导致编译错误。
作为返回值
指向一个local object
可以用作链式函数
#include <memory> #include <iostream> using namespace std; std::unique_ptr<Cat> fun5(){ std::unique_ptr<Cat> p = make_unique<Cat>(); cout << "address: " << p->get() << endl; // 指向本地堆地址 return p; } int main() { fun5()->get(); // 调用返回的 unique_ptr 的成员函数 cout << "address: " << fun5()->get() << endl; return 0; }可以直接通过函数返回的unique_ptr调用其成员函数,这得益于返回值优化(RVO)或移动语义,所有权被安全地转移给调用者。
2. 引用计数智能指针:shared_ptr
shared_ptr维护一个引用计数器,该计数器与所管理的动态对象内存相关联。
当发生拷贝构造或拷贝赋值时,计数器加 1;当shared_ptr被销毁或重置时,计数器减 1。
可通过use_count()成员函数获取当前引用计数值。
shared_ptr通常被称为共享指针,它允许多个指针共享同一对象的所有权。- 与
unique_ptr独占所有权不同,shared_ptr支持共享所有权。
2.1 创建 shared_ptr
#include <memory> #include <iostream> using namespace std; int main() { std::shared_ptr<Cat> p1 = make_shared<Cat>(); //std::shared_ptr<Cat> p1 = make_shared<Cat>{new Cat()}; cout << "p1 use count :" << p1.use_count() << endl; // 输出为 1 std::shared_ptr<Cat> p2 = p1; std::shared_ptr<Cat> p3 = p1; cout << "p1 use count :" << p1.use_count() << endl; // 输出为 3 cout << "p2 use count :" << p2.use_count() << endl; // 输出为 3 cout << "p3 use count :" << p3.use_count() << endl; // 输出为 3 p1 = nullptr; cout << "p1 use count :" << p1.use_count() << endl; // 输出为 0 cout << "p2 use count :" << p2.use_count() << endl; // 输出为 2 cout << "p3 use count :" << p3.use_count() << endl; // 输出为 2 p2.reset(); cout << "p2 use count :" << p2.use_count() << endl; // 输出为 0 cout << "p3 use count :" << p3.use_count() << endl; // 输出为 1 }对象只会被销毁一次。当没有任何shared_ptr指向该对象,即引用计数变为 0 时,对象才会被自动销毁。
2.2 shared_ptr与函数
shared_ptr值传递
函数内部计数器+1
#include <memory> #include <iostream> using namespace std; void fun1(std::shared_ptr<Cat> p) { cout << "p use count :" << p.use_count() << endl; // 输出为 2 } int main() { std::shared_ptr<Cat> p1 = make_shared<Cat>{new Cat()}; fun1(p1); count << "p1 use count :" << p1.use_count() << endl; //输出为1 return 0; }shared_ptr引用传递
const表示不可改变指向
#include <memory> #include <iostream> using namespace std; #include <memory> #include <iostream> using namespace std; void fun2(std::shared_ptr<Cat> &p) { cout << "p use count :" << p.use_count() << endl; // 输出为 1 p.reset(new Cat()); // p1 的指向会被改变 } void fun3(const std::shared_ptr<Cat> &p) { cout << "p use count :" << p.use_count() << endl; // 输出为 1 } int main() { std::shared_ptr<Cat> p1 = make_shared<Cat>{new Cat()}; fun2(p1); fun3(p1); cout << "p1 use count :" << p1.use_count() << endl; // 输出为 1 return 0; }作为返回值传递
#include <memory> #include <iostream> using namespace std; std::shared_ptr<Cat> fun5() { std::shared_ptr<Cat> p = std::make_shared<Cat>(new Cat()); return p; // 返回值会触发移动语义或拷贝省略,引用计数通常不变 } int main() { cout << "p1 use count :" << fun5().use_count() << endl; // 输出为 1 return 0; }shared_ptr与unique_ptr
不能将shared_ptr转换为unique_ptr
unique_ptr可以转换为shared_ptr(通过std::move)
#include <memory> #include <iostream> using namespace std; std::unique_ptr<Cat> fun() { std::unique_ptr<Cat> p = std::make_unique<Cat>(); return p; } int main() { std::unique_ptr<Cat> p1 = std::make_unique<Cat>(); std::shared_ptr<Cat> p2 = std::move(p1); std::cout << "p2 use count: " << p2.use_count() << std::endl; // 输出为 1 std::shared_ptr p3 = fun(); if (p3) { std::cout << "p3 use count: " << p3.use_count() << std::endl; // 输出为 1 } return 0; }weak_ptr
weak_ptr不拥有所指对象的所有权,因此不能直接使用->运算符或解引用运算符*来访问对象。
#include <memory> #include <iostream> using namespace std; int main() { std::shared_ptr<Cat> p1 = std::make_shared<Cat>(); std::weak_ptr<Cat> w_p1(p1); // 从 shared_ptr 创建 weak_ptr std::shared_ptr<Cat> p2 = w_p1.lock(); // 尝试提升为 shared_ptr,若对象仍存在则成功 return 0; }可以通过lock()成员函数尝试获取一个对应的shared_ptr。如果原始对象仍然存在(即其引用计数未降为零),则提升成功;否则返回一个空的shared_ptr。