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;

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。