C++11智能指针

目录

裸指针可能发生的问题:

 智能指针:

 RALL技术

智能指针需要解决的两件事情:

不带引用计数的智能指针

auto_ptr

scoped_ptr

unique_ptr

带引用计数的智能指针

shared_ptr

weak_ptr

 智能指针的交叉引用(循环引用)问题


裸指针可能发生的问题:

        1.忘记释放资源,导致资源泄露(常发生内存泄漏问题);

        2.写了释放内存部分,但由于程序逻辑满足条件可能在释放内存之前就return结束运行;

        3.多次释放同一块内存,产生野指针;

        2.由于程序执行出现异常,释放内存的部分没执行到;

 智能指针:

        自己能做到资源的自动释放:

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T *ptr = nullptr) :mptr(ptr) {}
	~CSmartPtr() { delete mptr; }
private:
	T *mptr;
};

int main()
{
	CSmartPtr<int> ptr(new int);
	/*其它的代码...*/

	/*由于ptr是栈上的智能指针对象,不管是函数正常执行完,还是运行过程中出现
	异常,栈上的对象都会自动调用析构函数,在析构函数中进行了delete
	操作,保证释放资源*/
	return 0;
}

1)智能指针体现在把裸指针进行了一次面向对象的封装,在构造函数中初始化资源地址,在析构函数中负责释放资源
2)利用栈上的对象出作用域自动析构这个特点,在智能指针的析构函数中保证释放资源

        智能指针一般都是定义在栈上的

 RAII技术

        确保对象被创建时获取资源,对象被销毁时自动释放资源,从而避免资源泄露的发生。通常是在对象的构造函数中获取资源,并在析构函数中释放资源来实现。

智能指针需要解决的两件事情:

  1. 怎么解决指针的浅拷贝问题
  2. 多个智能指针指向同一个资源的时候,怎么保证资源只释放一次,而不是每个智能指针都释放一次,造成代码运行不可预期的严重后果

不带引用计数的智能指针

auto_ptr

int main()
{
	auto_ptr<int> p1(new int);
	/*
	经过拷贝构造,p2指向了new int资源,
	p1现在为nullptr了,如果使用p1,相当于访问空指针了,很危险
	*/
	auto_ptr<int> p2 = p1;
	*p1 = 10;
	return 0;
}

         auto_ptr智能指针不带引用计数,那么它处理浅拷贝的问题,是直接把前面的auto_ptr都置为nullptr,只让最后一个auto_ptr持有资源。

scoped_ptr

scoped_ptr ( const scoped ptr<T>&) = delete;
scoped_ptr<T>& operator=(const scoped ptr<T>&) = delete;

        优化了拷贝构造函数和operator=赋值函数,因此从根本上杜绝了智能指针浅拷贝的发生,所以scoped_ptr也是不能用在容器当中的,如果容器互相进行拷贝或者赋值,就会引起scoped_ptr对象的拷贝构造和赋值,这是不允许的,代码会提示编译错误。 

unique_ptr

        让一个智能指针管理资源

        unique_ptr有一点和scoped_ptr做的一样,就是去掉了拷贝构造函数和operator=赋值重载函数,禁止用户对unique_ptr进行显示的拷贝构造和赋值,防止智能指针浅拷贝问题的发生

unique_ptr ( const scoped ptr<T>&) = delete;
unique_ptr<T>& operator=(const scoped ptr<T>&) = delete;

        但是unique_ptr提供了带右值引用参数的拷贝构造和赋值,unique_ptr智能指针可以通过右值引用进行拷贝构造和赋值操作

unique_ptr (unique_ptr<T> &&src);
unique_ptr<T>& operator=(unique_ptr<T> &&src);
unique_ptr<int> ptr(new int);
unique_ptr<int> ptr2 = std::move(ptr); // 使用了右值引用的拷贝构造
ptr2 = std::move(ptr); // 使用了右值引用的operator=赋值重载函数

带引用计数的智能指针

        带引用计数:多个智能指针可以管理同一个资源,每一个智能指针都会给资源的引用计数加1,当一个智能指针析构时,同样会使资源的引用计数减1,这样最后一个智能指针把资源的引用计数从1减到0时,就说明该资源可以释放了,由最后一个智能指针的析构函数来处理资源的释放问题。

         要对资源的引用个数进行计数,那么大家知道,对于整数的++或者- -操作,它并不是线程安全的操作,因此shared_ptr和weak_ptr底层的引用计数已经通过CAS操作,保证了引用计数加减的原子特性,因此shared_ptr和weak_ptr本身就是线程安全的带引用计数的智能指针。(atomic_int CAS)

shared_ptr

强智能指针 可以改变资源的引用计数

private:
	/*
	下面这两个是shared_ptr的成员变量,_Ptr是指向内存资源的指针,_Rep是
	指向new出来的计数器对象的指针,该计数器对象包含了资源的一个引用计数器count
	*/
	element_type * _Ptr{nullptr};
	_Ref_count_base * _Rep{nullptr};

weak_ptr

弱智能指针 不会改变资源的引用计数

 智能指针的交叉引用(循环引用)问题

#include <iostream>
#include <memory>
using namespace std;

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	shared_ptr<B> _ptrb; // 指向B对象的智能指针
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	shared_ptr<A> _ptra; // 指向A对象的智能指针
};
int main()
{
	shared_ptr<A> ptra(new A());// ptra指向A对象,A的引用计数为1
	shared_ptr<B> ptrb(new B());// ptrb指向B对象,B的引用计数为1
	ptra->_ptrb = ptrb;// A对象的成员变量_ptrb也指向B对象,B的引用计数为2
	ptrb->_ptra = ptra;// B对象的成员变量_ptra也指向A对象,A的引用计数为2

	cout << ptra.use_count() << endl; // 打印A的引用计数结果:2
	cout << ptrb.use_count() << endl; // 打印B的引用计数结果:2

	/*
	出main函数作用域,ptra和ptrb两个局部对象析构,分别给A对象和
	B对象的引用计数从2减到1,达不到释放A和B的条件(释放的条件是
	A和B的引用计数为0),因此造成两个new出来的A和B对象无法释放,
	导致内存泄露,这个问题就是“强智能指针的交叉引用(循环引用)问题”
	*/
	return 0;
}


//代码打印结果:
A()
B()
2
2

定义对象时,用强智能指针shared_ptr,在其它地方引用对象时,使用弱智能指针weak_ptr

弱智能指针weak_ptr区别于shared_ptr之处在于:

        1.weak_ptr不会改变资源的引用计数,只是一个观察者的角色,通过观察shared_ptr来判定资源是否存在
        2.weak_ptr持有的引用计数,不是资源的引用计数,而是同一个资源的观察者的计数
        3.weak_ptr没有提供常用的指针操作,无法直接访问资源,需要先通过lock方法提升为shared_ptr强智能指针,才能访问资源。

上述代码修改:

#include <iostream>
#include <memory>
using namespace std;

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	weak_ptr<B> _ptrb; // 指向B对象的弱智能指针。引用对象时,用弱智能指针
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	weak_ptr<A> _ptra; // 指向A对象的弱智能指针。引用对象时,用弱智能指针
};
int main()
{
    // 定义对象时,用强智能指针
	shared_ptr<A> ptra(new A());// ptra指向A对象,A的引用计数为1
	shared_ptr<B> ptrb(new B());// ptrb指向B对象,B的引用计数为1
	// A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变
	ptra->_ptrb = ptrb;
	// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变
	ptrb->_ptra = ptra;

	cout << ptra.use_count() << endl; // 打印结果:1
	cout << ptrb.use_count() << endl; // 打印结果:1

	/*
	出main函数作用域,ptra和ptrb两个局部对象析构,分别给A对象和
	B对象的引用计数从1减到0,达到释放A和B的条件,因此new出来的A和B对象
	被析构掉,解决了“强智能指针的交叉引用(循环引用)问题”
	*/
	return 0;
}

//代码打印如下:
A()
B()
1
1
~B()
~A()
### C++11智能指针的用法和特性 #### 使用场景与优势 C++11引入了三种主要类型的智能指针:`std::unique_ptr`, `std::shared_ptr` 和 `std::weak_ptr`。这些智能指针通过自动管理对象生命周期来提高程序安全性并减少内存泄漏的风险[^1]。 #### unique_ptr 的定义与使用方法 `std::unique_ptr` 是一种独占所有权的智能指针,意味着同一时间只有一个 `unique_ptr` 可以指向某个特定的对象实例。当 `unique_ptr` 被销毁时,它所拥有的资源也会被释放。 ```cpp #include <memory> // 创建一个指向整数的唯一指针 std::unique_ptr<int> p(new int(42)); ``` #### shared_ptr 的定义与使用方法 对于需要多个所有者共享同一个对象的情况,则可以使用 `std::shared_ptr`. 它内部实现了一个引用计数机制,每当有一个新的 `shared_ptr` 复制该指针时就会增加计数值;而当最后一个持有此指针的对象超出作用域或显式删除时才会真正释放关联的堆分配空间. ```cpp #include <memory> void foo(std::shared_ptr<int> sp){ *sp += 5; } int main(){ auto ptr = std::make_shared<int>(10); foo(ptr); // 不会触发深拷贝,因为只是传递给函数参数 } ``` #### weak_ptr 的定义与使用方法 为了避免循环引用问题,在某些情况下还需要配合 `std::weak_ptr` 来观察而不控制目标对象的生命期。这通常发生在两个对象相互保持对方存活的情况下(即A持有了B,B也持有了A),此时如果都采用强引用(`shared_ptr`)则会造成无法回收的问题. ```cpp class Node { public: ~Node() { /* ... */ } private: std::vector<std::weak_ptr<Node>> neighbors_; // 邻居节点列表只做观测用途 }; ``` 以上就是关于C++11智能指针的主要介绍以及它们各自的特性和应用场景.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值