文章目录
智能指针
什么是智能指针
智能指针其实是⼀个类,实质就是重载了->和*操作符的类,由类来实现对内存的管理,确保即使有异常产⽣,也可以通过智能指针类的析构函数完成内存的释放。智能指针的⾏为类似常规指针,重要的区别在于智能指针负责⾃动释放所指向的对象。
STL提供给我们提供了四种智能指针类型,除了下边列出的三种,还有C++98标准auto_ptr智能指针,C+11已将将其摒弃。C++11 标准新引⼊了 这 3 个智能指针:
shared_ptr : 允许多个智能指针指向同⼀个对象
unique_ptr : ⼀个指针“独占”所指向的对象
weak_ptr : 弱引⽤,指向shared_ptr所管理的对象
unique_ptr
unique_ptr
它提供了对动态分配内存的独占所有权的管理。unique_ptr
确保同⼀时间只有⼀个unique_ptr
实例可以拥有⼀个对象的指针,从⽽避免了资源的多重释放问题。当 std::unique_ptr
被销毁时,它所指向的对象也会被⾃动销毁。unique_ptr
智能指针是以模板类的形式提供的,unique_ptr<T>
(T 为指针所指数据的类型),定义在<memory>
头⽂件,并位于 std 命名空间中。
特点:
● 轻量级:没有额外的引⽤计数开销,因此效率较⾼。
● 不可复制: std::unique_ptr 不能被复制,但可以通过 std::move 转移
所有权。
● ⾃动释放: 当 unique_ptr 离开作⽤域时,它所管理的对象会被⾃动删除。
基础操作如下:
1.创建unique_ptr对象
#include <iostream>
#include<string>
#include<memory>
using namespace std;
class stu
{
public:
string name;
int age;
stu(string name, int age)
{
this->name = name;
this->age = age;
}
void test()
{
cout << "fron stu method test" << endl;
}
~stu()
{
cout << "stu 析构函数" << endl;
}
};
//1.创建空智能指针对象
unique_ptr<int> ptr; //构建⼀个空的唯⼀智能指针对象
std::unique_ptr<int> p2(nullptr);
//2.初始化智能指针对象
//unique_ptr<int>myptr(new int);//创建⼀个⾮空的智能指针对象,由该对象实现托管对象
的内存释放
unique_ptr<int>myptr(new int(12));
unique_ptr<stu>ptr1(new stu("yq", 12));//构建⼀个智能指针对象指向⽤户新建的堆对
象。
2.访问所指向对象
通过解引⽤运算符 * 可以访问 std::unique_ptr 所指向的对象。或使⽤箭头运算符 ->
进⾏指向对象成员的访问
int* pt = new int(3); //
unique_ptr<int> myptr(pt);
//通过智能指针对绑定的单个对象进⾏操作 假如你绑定的stu类型 此时访问成员: myptr->age
cout << *myptr << endl;
*myptr = *myptr + 100;//修改所指向对象
//通过指向运算符对托管对象的成员进⾏访问
unique_ptr<stu> ptr(new stu("yq", 12));
cout << ptr->age << endl;
cout << ptr->name << endl;
3.智能指针绑定动态数组
声明形式:unique_ptr<T[ ]> up(p)
unique_prt< int[ ] > up( new int[10] );
for( size_t i = 0 ; i ! = 10 ; ++ i )
up[ i ]=i; //对其初始化 //cout<<up[ i ] <<endl; //
//或者绑定时直接初始化
unique_ptr<int[]> p(new int[3]{11,22,33});
4.转移 unique_ptr 对象的所有权
不能对unique_ptr 对象进⾏拷⻉,下列语句⽆法执⾏
unique_ptr taskPtr2 = taskPtr; // ⾮法
但是可以移动它,所谓“移动”,其实就是转移所有权,应该使⽤std::move;
注意,两个unique_ptr不能指向同⼀个内存地址。
unique_ptr<Task> p1(new Task(23));
unique_ptr<Task>p2;
//p2 = p1或者unique_ptr<Task>p2(p1);错的 unique_ptr对象不能拷⻉,但是可以移
动
p2 = move(p1);
cout << p2->mId << endl; //23
将与 p1 绑定的 raw 指针转移给 p2,在这之后,p1 变为空
//检查 unique_ptr 对象是否为空
if (ptr1 == nullptr) //或者if(!ptr1)
cout << "指针为空" << endl;
else
cout << "指针不为空" << endl;
5.释放关联的原始指针
unique_ptr 的 release() 函数可以直接将对绑定 raw 指针的所有权释放,该函数会将绑定的 raw 指针返回,注意,这⾥的释放所有权,并没有delete原始指针,⽽reset()会delete原始指针。调⽤ unique_ptr 的 reset() ⽅法将删除与之绑定的 raw 指针,并且将 unique_ptr对象置空.
unique_ptr<Task> p1(new Task(23));
Task *p2=p1.release(); //p1 变为空,并且其绑定的 raw 指针被赋值给 p2。
if(p1 == nullptr)
std::cout << "p1 is empty" << std::endl;
cout << p2->mId << endl; //23
// 此时需要⼿动删除⽤完的指针
delete p2;
当指针指针对象超出作⽤域时会⾃动析构,unique_ptr对象的析构函数中会delete其关联指针,这样就相当于替我们执⾏了delete堆内存上的对象。
6.get()⽅法仅仅返回关联指针,但不会释放所有权
int* p = new int;
unique_ptr<int> ptr1(p);
cout << ptr1 <<":" <<p<<"#"<<ptr1.get() << endl;
>> 016BB98:0016BB98#0016BB98
shared_ptr
当多个对象需要共享同⼀个资源,并且在所有使⽤该资源的对象都不再需要它时,资源才应该被释放,这时 std::shared_ptr 就⾮常有⽤。
shared_ptr基于”引⽤计数”模型实现,多个shared_ptr可指向同⼀个动态对象,并维护⼀个共享的引⽤计数器,记录了引⽤同⼀对象的shared_ptr实例的数量。当最后⼀个指向动态对象的shared_ptr销毁时,会⾃动销毁其所指对象(通过delete操作符)。可共享指针对象,可以赋值给shared_ptr或weak_ptr。
3)shared_ptr
所属头⽂件:#include
所属命名空间及标识符:using std::shared_ptr;
所属版本:C++11
g++启⽤版本命令:g+±std=c++11-c -o
- 定义智能指针对象
std::shared_ptr<int> p1; //不传⼊任何实参
std::shared_ptr<int> p2(nullptr); //传⼊空指针 nullptr
注意,空的 shared_ptr 指针,其初始引⽤计数为 0,⽽不是 1。
- 初始化shared_ptr类对象
可以使⽤make_shared在动态内存中分配⼀个对象并初始化它, 返回指向此对象的shared_ptr。除此之外还可以通过构造函数、赋值函数初始化智能指针。
格式:shared_ptr<类型> 指针名 = make_shared<类型>();
//⽅式1:⽤具体的值初始化智能指针对象指向内存
shared_ptr<int> my1(new int(11));//通过具体的值直接初始化共享智能指针
//⽅式2 :⽤另⼀个智能指针对象初始化智当前指针对象
shared_ptr<int> p2=p1;
cout<<*p1<<endl;
//⽅式3: make_shared时,必须指定想要创建的对象的类型,定义⽅式与模板类相同
shared_ptr<int> iptr = make_ptr<int>(); // 使⽤int的默认构造函数,初始化shared
_ptr对象的内容
shared_ptr<int> iptr = make_ptr<int>(36); // 使⽤int的重载构造函数,初始化shar
ed_ptr对象的内容为36
make_shared的标准库的模板函数,推荐这种⽅式初始化指针对象,因为使⽤std::make_shared能够减⼩了内存分配的次数,提升效率。
可以通过成员函数use_count()来查看资源的所有者个数。
//get⽅法可以获取智能指针绑定对象的地址
shared_ptr<Task> p1(new Task(123));//调⽤构造函数输出Test create
shared_ptr<Task> p2(new Task(456));//调⽤构造函数输出 Test creat
cout <<p1->mId << endl; //输出123
cout << p2 <<":"<< p2.get() << endl;//返回保存的指针
cout << p2.use_count() << endl;//显示此时资源被⼏个指针共享,输出1
shared_ptr<Task> p3 = p2; //增加⼀个引⽤
cout << p2.use_count() << endl;
//重新绑定对象,绑定⼀个空对象,当时此时指针指向的对象还有其他指针能指向就不会释放该对象
的内存空间
p2.reset();
cout << p3.use_count() << endl;
cout << p3->mId << endl; //456
>>>
Task::Constructor
Task::Constructor
123
00BFBEB8:00BFBEB8
1
2
1
456
Task::Destructor
Task::Destructor
递增的情况有三种(这⾥类似复制构造函数,存在的情形):
a.⽤⼀个shared_ptr初始化另外⼀个shared_ptr
b.作为函数参数传参(拷⻉实参对象)
c.作为函数返回值(返回出来还是要赋值)
● 访问绑定对象的值或成员通过*或->
shared_ptr<stu> share(new stu("yq", 33));
cout << share->name <<":"<< (*share).name<< endl;
共享指针绑定到动态数组
//将共享智能绑定到动态数组
shared_ptr<int[]>ss(new int[3]{ 11,22,33 });
for (int i = 0; i < 3; i++)
{
cout << ss[i]<< endl;
}
shared_ptr使⽤注意:
1)同⼀普通指针不能同时为多个 shared_ptr 对象初始化,否则会导致程序发⽣异
常(原因析构是会出问题)
2)不能⽤ delete get()返回的指针
3)不使⽤get()初始化或reset另⼀个智能指针
3)如果你使⽤get()返回的指针,记住当最后⼀个对应的智能指针销毁后,你的指针就变为⽆效了。
4)weak_ptr
std::weak_ptr 是 C++ 标准库 头⽂件中提供的⼀种智能指针,它是⼀种弱引⽤,⽤于配合 std::shared_ptr 使⽤,主要⽤于解决 std::shared_ptr 可能存在的循环引⽤问题,避免内存泄漏。以下是 std::weak_ptr的⼀些常⻅使⽤场景:
#include <iostream>
#include <memory>
class B;
class A {
public:
std::weak_ptr<B> bPtr;
~A() { std::cout << "A destroyed" << std::endl; }
};
class B {
public:
std::shared_ptr<A> aPtr;
~B() { std::cout << "B destroyed" << std::endl; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->bPtr = b;
b->aPtr = a;
return 0;
}
当两个或多个对象通过 std::shared_ptr 相互引⽤,形成循环引⽤时,会导致这些对象的引⽤计数永远不会变为 0,从⽽造成内存泄漏。 std::weak_ptr 可以打破这种循环引⽤,因为它不会增加对象的引⽤计数。