C++智能指针,RAII(资源获取即初始化) 原则
一、unique_ptr
尽量使用unique_ptr替代裸指针
一个unique_ptr独享它指向的对象,不能直接(使用reset才可以)将一个unique_ptr赋值给其他变量,不过shared_ptr可以。也就是说,同时只有一个unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。如果一个类中包含unique_ptr,那么该类也不能使用拷贝和赋值。
数组版本
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(5); //相当于 int ptr[5] = {0};
局部智能指针不能作为函数返回值,因为智能指针的作用域有限
1. 获取裸指针 get()
返回指向被管理对象的指针
2. 替换被管理对象 reset()
注意:因为一个unique_ptr独享它指向的对象,因此替换被管理的对象时必须释放之前对象的所有权
std::unique_ptr<int> p1 = std::make_unique<int>(2);
std::unique_ptr<int> p2 = std::make_unique<int>(3);
p1 = p2;//错误
p1.reset(p2); //错误
p1.reset(p2.release()); //正确,此时p1管理的是大小为3的int数组对象
3. 释放被管理对象 release()
unique_ptr被销毁时(如离开作用域),对象也就自动释放了,也可以通过其他方式下显式释放对象。如:
up = nullptr; //置为空,释放up指向的对象
up.release(); //放弃控制权,返回裸指针,并将up置为空
up.reset(); //释放up指向的对象
release不会调用析构,只是释放控制权
4. std::unique_ptr和类的前置声明
//test.h
class A;
class B
{
std::unique<A> m_a;
};
智能指针和类的前置声明
VS报错:can't delete an incomplete type 解决方法:将类的构造函数和析构函数在cpp文件中实现。如果这个类还包含了一个前置声明的类,将这个前置声明的类的头文件包含到cpp文件(A包含了B,B包含了C,需要在A.cpp包含C.h)。
二、share_ptr
尽量少用share_ptr
shared_ptr是支持共享所有权的引用计数型指针,“共享”指的是允许多个指针指向同一个资源,shared_ptr对象可以被拷贝与赋值,“引用计数”指的是它可以记录当前指向同一资源的指针的数量, 当计数为0的时候才会释放动态分配的对象。对shared_ptr循环引用会导致因为无法释放资源引发的内存泄漏。典型的如双链表。不要把一个裸指针给多个shared_ptr对象管理
#include <iostream>
#include <memory>
class B;
class A
{
public:
A() = default;
~A() { std::cout << "~A\n"; }
std::shared_ptr<B> _b;
};
class B
{
public:
B() = default;
~B() { std::cout << "~B\n"; }
std::shared_ptr<A> _a;
};
int main()
{
auto pa = std::make_shared<A>();
auto pb = std::make_shared<B>();
pa->_b = pb;
pb->_a = pa;
std::cout << "A use count:" << pa.use_count() << std::endl;
std::cout << "B use count:" << pb.use_count() << std::endl;
}
输出如下
A use count:2
B use count:2
因为引用计数都是2,所以在main函数结束的时候,不会调用类A和类B的析构函数,就出现了内存泄漏。上面产生内存泄漏的原因,就是我们常说的循环引用。
三、weak_ptr
weak_ptr就是为了辅助shared_ptr的,它本身不能直接指向原生指针对象,只能指向shared_ptr对象。
weak_ptr总结
-
weak_ptr虽然是一个模板类,但是不能用来直接定义指向原始指针的对象。
-
weak_ptr接受shared_ptr类型的变量赋值,但是反过来是行不通的,需要使用lock函数。
-
weak_ptr设计之初就是为了服务于shared_ptr的,所以不增加引用计数就是它的核心功能。
-
由于不知道干了什么之后weak_ptr所指向的对象就会被析构掉,所以使用之前请先使用expired函数检测一下。
四、auto_ptr
被摒弃 https://www.cnblogs.com/lanxuezaipiao/p/4132096.html
示例代码
点击查看代码
#include <iostream>
#include <memory>
class A
{
public:
std::shared_ptr<int> sharedInt = std::make_shared<int>(1);
//std::unique_ptr<int> uniqueInt = std::make_unique<int>(2);
void show() { std::cout << *sharedInt << "\n"; }
};
int main()
{
A a;
a.show();
A a1 = a; //如果类A中有unique_ptr报错
A a2 = a1;
std::cout << a.sharedInt.use_count(); //3
///< Shared_ptr
auto s1 = std::make_shared<int>(1);
auto s2 = std::make_shared<int>(2);
auto s3 = std::make_shared<int>(3);
std::shared_ptr<int> ps1;
std::shared_ptr<int> ps2;
auto c1 = s1.use_count(); //1
auto c2 = s2.use_count(); //1
auto c3 = s3.use_count(); //1
auto c4 = ps1.use_count(); //0
auto c5 = ps2.use_count(); //0
ps1 = s1; //共享s1指向的对象(unique_ptr不支持)
auto c6 = ps1.use_count(); //2
auto c7 = s1.use_count(); //2
ps2 = s1;
auto c8 = s1.use_count(); //3
auto c9 = ps2.use_count(); //3
///< unique_ptr
std::unique_ptr<int> pu1 = std::make_unique<int>(2);
std::unique_ptr<int> pu2 = std::make_unique<int>(3);
//p1.reset(p2); //错误
//std::unique_ptr<int> pu6 = pu1; //错误,因为unique_ptr独享它指向的对象(可以用shared_ptr共享)
pu1.reset(pu2.release()); //正确,此时p1管理的是值为3的int指针
auto pu1Value1 = *pu1.get();
std::unique_ptr<int[]> pu3 = std::make_unique<int[]>(9); //大小为9的int数组
std::unique_ptr<int[]> pu4 = std::unique_ptr<int[]>(new int[3]); //大小为3的int数组,不推荐使用new
std::unique_ptr<int[]> pu5;
int arr[5] = { 0 };
int* pu6 = new int[8];
pu4.reset(arr);
pu5.reset(pu6);
//std::unique_ptr<int[]> pu7(pu3); //错误
std::unique_ptr<int[]> pu7(std::move(pu3));//正确
//pu5.reset(std::move(pu3)); //错误
//pu4.reset(pu3); //错误
pu5.reset(pu3.release()); //正确
(*pu1) = 99; //给pu1赋值
auto pu1Value2 = *pu1;
system("pause");
return 0;
}