c++11之智能指针(详解)

文章目录

  • 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,解决循环引用的问题

为什么使用智能指针?

解决问题:

  1. 自动内存管理:智能指针(如 std::unique_ptr、std::shared_ptr 和 std::weak_ptr)在析构时会自动释放所管理的内存,从而防止内存泄漏。
  2. 异常安全:当异常发生时,智能指针能够确保即使在构造函数或析构函数抛出异常的情况下,也能正确释放内存。
  3. 提供共享和独占的语义:
    std::unique_ptr 提供独占所有权的语义,即同一时间只能有一个智能指针指向同一个对象。
    std::shared_ptr 提供共享所有权的语义,允许多个智能指针共享同一个对象的所有权,并能够跟踪引用计数,自动释放内存。
  4. 与标准容器兼容:智能指针可以与标准容器(如 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.
c++11之智能指针(详解)_第1张图片

初始化 shared_ptr 智能指针:

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 
#include 
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 
#include 
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 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 使用的注意事项

  1. 不要使用一个原始指针初始化多个shared_ptr
int *ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); // 逻辑错误
  1. 不要在函数实参中创建shared_ptr
function(shared_ptr<int>(new int), g()); //有缺陷
//正确的做法:
shared_ptr<int> p(new int);
function(p, g());
  1. 通过shared_from_this()返回this指针
#include 
#include 
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 
#include 
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;
}
  1. 避免循环引用
#include 
#include 
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 
#include 
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;
}

你可能感兴趣的:(c/c++,c++,开发语言)