c++11之智能指针(详解)
CSDN 2024-09-10 09:05:02 阅读 97
文章目录
c++11的智能指针是什么?为什么使用智能指针?怎么使用智能指针?头文件:#include < memory >shared_ptr 、unique_ptr 和 weak_ptr的区别:初始化 shared_ptr 智能指针:初始化 unique_ptr 指针自定义删除器weak_ptr的基本使用
智能指针进阶shared_ptr 使用的注意事项
c++11的智能指针是什么?
c++里面有四个智能指针:auto_ptr, shared_ptr, unique_ptr, weak_ptr
其中第一个auto_ptr被c++11弃用。
各指针的特点:
unique_ptr 独占对象的所有权,由于没有引用计数,因此性能较好
shared_ptr 共享对象的所有权,但性能略差
weak_ptr 配合shared_ptr,解决循环引用的问题
为什么使用智能指针?
解决问题:
自动内存管理:智能指针(如 std::unique_ptr、std::shared_ptr 和 std::weak_ptr)在析构时会自动释放所管理的内存,从而防止内存泄漏。异常安全:当异常发生时,智能指针能够确保即使在构造函数或析构函数抛出异常的情况下,也能正确释放内存。提供共享和独占的语义:
std::unique_ptr 提供独占所有权的语义,即同一时间只能有一个智能指针指向同一个对象。
std::shared_ptr 提供共享所有权的语义,允许多个智能指针共享同一个对象的所有权,并能够跟踪引用计数,自动释放内存。与标准容器兼容:智能指针可以与标准容器(如 std::vector、std::list 等)一起使用,使得容器中的对象也可以自动管理内存。
怎么使用智能指针?
头文件:#include < memory >
shared_ptr 、unique_ptr 和 weak_ptr的区别:
·unique_ptr是一个独占型的智能指针,不能将其赋值给另一个unique_ptr,不能和其它指针共享内存
·unique_ptr需要确定删除器的类型
·shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。再最后一个shared_ptr析
构的时候,内存才会被释放。
·即shared_ptr指针由两部分构成:一个是指向数据的裸指针,一个是指向内部隐藏的、共享的管理对象。(Control Block),每当拷贝一个指针时,Control Block里面的计数器值+1(初始值是0),销毁一个指针就-1,如果减到计数值为0,那么就会回收 裸指针指向的数据块内存,并且销毁最后一个指针。
·weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。
·weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。
·简单来说:weak_ptr 指针是配合 shared_ptr 使用的,目的是解决shared_ptr的循环引用造成的问题。(后面会分析这个问题)
下图是shared_ptr 的内部原理图:
每多一个shared_ptr指针,Reference Count += 1.
初始化 shared_ptr 智能指针:
<code>auto sp1 = make_shared<int>(100);
// 或
shared_ptr<int> sp1 = make_shared<int>(100);
//相当于
shared_ptr<int> sp1(new int(100));
我们应该优先使用make_shared来构造智能指针,因为他更高效。
注意:不能将一个原始指针直接赋值给一个智能指针,例如,下面这种方法是错误的
std::shared_ptr<int> p = new int(1); //错误
更多的例子:
// 智能指针初始化
std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2 = p1;
std::shared_ptr<int> p3;
p3.reset(new int(1)); // 重置p3
// 直接判断智能指针是否为空:true 或 false,不同与普通指针需要判断nullptr
if(p3) {
cout << "p3 is not null";
// reset()的用法
// reset()不带参数,若智能指针p唯一指向该对象的指针,则释放内存,并置空。若智能指针p不是唯一指向该对象的指针,则引用计数减少1,同时将p置空。
// reset()带参数,若智能指针是唯一指向对象的指针,则释放并指向新的对象。若p不是唯一指针,则只减少引用计数,并将p指向新的对象(原来的对象依旧保留)。
}
int main()
{
std::shared_ptr<int> p1;
p1.reset(new int(1));
std::shared_ptr<int> p2 = p1;
// 引用计数此时应该是2
cout << "p2.use_count() = " << p2.use_count()<< endl;
p1.reset();
cout << "p1.reset()\n";
// 引用计数此时应该是1
cout << "p2.use_count()= " << p2.use_count() << endl;
if(!p1) {
cout << "p1 is empty\n";
}
if(!p2) {
cout << "p2 is empty\n";
}
p2.reset();
cout << "p2.reset()\n";
cout << "p2.use_count()= " << p2.use_count() << endl;
if(!p2) {
cout << "p2 is empty\n";
}
return 0;
}
//输出结果:
p2.use_count() = 2
p1.reset()
p2.use_count()= 1
p1 is empty
p2.reset()
p2.use_count()= 0
p2 is empty
初始化 unique_ptr 指针
unique_ptr<T> my_ptr(new T);
unique_ptr<T> my_other_ptr = my_ptr; // 报错,不能复制
unique_ptr不允许复制,但可以通过函数返回给其他的unique_ptr,还可以通过std::move来转移到其
他的unique_ptr,这样它原本就不再拥有原来指针的所有权了。例如
unique_ptr<T> my_ptr(new T); // 正确
unique_ptr<T> my_other_ptr = std::move(my_ptr); // 正确,my_ptr指向空,my_other_ptr指向T
unique_ptr<T> ptr = my_ptr; // 报错,不能复制
std::make_shared是c++11的一部分,但std::make_unique不是。它是在c++14里加入标准库的。
auto upw1(std::make_unique<Widget>()); // with make func
std::unique_ptr<Widget> upw2(new Widget); // without make func
更多例子:
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<int[]> p1(new int[10]);
shared_ptr<int[]> p2(new int[10]);
for(int i = 0; i < 10 ;i++){
p1[i] = i;
p2[i] = i;
cout<< p1[i] << endl;
cout<< p2[i] << endl;
}
// 自动回收内存
return 0;
}
}
自定义删除器
shared_ptr 和 unique_ptr 都可以自定义删除器来释放内存
#include <iostream>
#include <memory>
using namespace std;
void DeleteIntPtr(int *p) {
cout << "call DeleteIntPtr" << endl;
delete p;
}
int main()
{
shared_ptr<int> p(new int(1), DeleteIntPtr);
shared_ptr<int> ptr3(new int(1), [](int *p){ delete p;}); // 正确
// unique_ptr<int> ptr4(new int(1), [](int *p){delete p;}); // 错误,需要指定类型
std::unique_ptr<int, void(*)(int*)> ptr5(new int(1), [](int *p){ delete p;}); //正确
return 0;
}
weak_ptr的基本使用
std::weak_ptr<int> gw;
void f()
{
auto spt = gw.lock();
//通过expired()方法判断所观察资源是否已经释放
if(gw.expired()) {
cout << "gw无效,资源已释放";
}
else {
cout << "gw有效, *spt = " << *spt << endl;
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
f();
}
f();
return 0;
}
智能指针进阶
shared_ptr 使用的注意事项
不要使用一个原始指针初始化多个shared_ptr
int *ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); // 逻辑错误
不要在函数实参中创建shared_ptr
function(shared_ptr<int>(new int), g()); //有缺陷
//正确的做法:
shared_ptr<int> p(new int);
function(p, g());
通过shared_from_this()返回this指针
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
shared_ptr<A> GetSelf()
{
return shared_ptr<A>(this); // 不要这么做
}
~A()
{
cout << "Destructor A" << endl;
}
};
int main()
{
// 在这个例子中,由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间是没有任何关系
// 的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致"重复析构"的错误。
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2 = sp1->GetSelf();
r
#include <iostream>
#include <memory>
using namespace std;
class A: public std::enable_shared_from_this<A>
{
public:
shared_ptr<A>GetSelf()
{
return shared_from_this(); //
}
~A()
{
cout << "Destructor A" << endl;
}
};
int main()
{
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2 = sp1->GetSelf(); // ok
return 0;
}
避免循环引用
#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
std::shared_ptr<B> bptr;
~A() {
cout << "A is deleted" << endl;
}
};
class B {
public:
std::shared_ptr<A> aptr;
~B() {
cout << "B is deleted" << endl;
}
};
int main()
{
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
}
cout<< "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构成功
//循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导
//致两个指针都不会被析构,产生内存泄漏。
//解决的办法是把A和B任何一个成员变量改为weak_ptr,来破坏这个循环体
return 0;
}
使用weak_ptr打破循环:
#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
std::weak_ptr<B> bptr; // 修改为weak_ptr
~A() {
cout << "A is deleted" << endl;
}
};
class B {
public:
std::shared_ptr<A> aptr;
~B() {
cout << "B is deleted" << endl;
}
};
int main()
{
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
}
cout<< "main leave" << endl;
return 0;
}
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。