c++智能指针概念
1 智能指针的思想--C++RAII机制
RAII是Resource Acquisition Is Initialization(wiki上面翻译成 “资源获取就是初始化”)的简称,是C++语言的一种管理资源、避免泄漏的惯用法,利用的就是C++构造的对象最终会被析构销毁的原则。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,在对象生命期结束析构的时候,释放构造时获取的资源。
我们知道,c++动态内存分配需要为所有的new 分配的内存手动delete掉对应普通指针指向的内存资源,否则可用内存可能会随着代码的调用越来越少,最终导致程序崩溃。
我们也知道,当我们在一个函数内部使用局部变量,当退出了这个局部变量的作用域时,这个变量也就别销毁了;当这个变量是类对象时,这个时候,就会自动调用这个类的析构函数,而这一切都是自动发生的,不要程序员显式的去调用完成。
那么这个也太好了,智能指针思想就是这样去完成的。智能指针就是利用RAII(Resource Acquisition Is Initialization)机制对普通指针进行的一层封装。这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以安全的管理一个对象的生命周期。
智能指针 = 普通指针 + c++对象的构造/析构机制
2 智能指针种类
在c++中,智能指针一共定义了4种:
unique_ptr、shared_ptr、weak_ptr 和 auto_ptr。其中,auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用,重点学习 前两种。这些类型都定义在头文件<memory>
中。
std::weak_ptr: std::weak_ptr是一种弱引用智能指针,它用于解决std::shared_ptr可能导致的循环引用问题。std::weak_ptr可以观测到被std::shared_ptr管理的对象,但不拥有该对象,也不会增加引用计数。它可以通过std::shared_ptr进行构造或转换,用于临时访问被std::shared_ptr管理的对象,但不会延长对象的生命周期。
c++智能指针介绍
unique_ptr
unique_ptr是一种独占智能指针,它提供了对动态分配的对象的独占所有权。它不允许多个unique_ptr 指向同一块内存,因此不能进行拷贝,只能进行移动(转移交出所有权给另一个智能指针)。当std::unique_ptr超出其作用域时,或者通过std::move将所有权转移给其他std::unique_ptr时,它指向的对象也会被自动摧毁,帮助程序员实现了自动释放的功能。
unique_ptr 也可能还未指向对象,这时的状态被称为 empty。
unique_ptr对象封装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。
创建unique_ptr:
可以先看一个简单的 unique_ptr 使用的例子:
动态创建一个一维数组
unique_ptr<char> p(new char[10]);
此时我们只要unique_ptr创建成功,unique_ptr对应的析构函数都能保证被调用,从而保证申请的动态资源能被释放掉。
对比-用普通指针用法:
char* p = new char[10];//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址 delete[] p;
下面可以更详细的了解unique_ptr的构造和析构原型:
unique_ptr的构造方法:
// non-specialized template <class T, class D = default_delete<T>> class unique_ptr; // array specialization template <class T, class D> class unique_ptr<T[],D>;
其中 T 指其管理的对象类型,D 指该对象销毁时所调用的释放方法,可以使用自定义的删除器,他也有一个默认的实现,即 detele 操作。
// unique_ptr constructor example #include <iostream> #include <memory>int main () {std::default_delete<int> d;std::unique_ptr<int> u1;std::unique_ptr<int> u2 (nullptr);std::unique_ptr<int> u3 (new int);std::unique_ptr<int> u4 (new int, d);std::unique_ptr<int> u5 (new int, std::default_delete<int>());std::unique_ptr<int> u6 (std::move(u5));std::unique_ptr<int> u7 (std::move(u6));std::unique_ptr<int> u8 (std::auto_ptr<int>(new int));std::cout << "u1: " << (u1?"not null":"null") << '\n';std::cout << "u2: " << (u2?"not null":"null") << '\n';std::cout << "u3: " << (u3?"not null":"null") << '\n';std::cout << "u4: " << (u4?"not null":"null") << '\n';std::cout << "u5: " << (u5?"not null":"null") << '\n';std::cout << "u6: " << (u6?"not null":"null") << '\n';std::cout << "u7: " << (u7?"not null":"null") << '\n';std::cout << "u8: " << (u8?"not null":"null") << '\n';return 0; }//执行结果: u1: null u2: null u3: not null u4: not null u5: null u6: null u7: not null u8: not null
注意:不能像下面这样写。new得到的是一个普通指针,智能指针本质是一个对象,不能直接赋值,而是作为参数传递。
std::unique_ptr<int> p = new int(5)
但可以
shared_ptr<int> p = make_shared<int>(5);
自定义unique_ptr的析构方法 std::unique_ptr::~unique_ptr
// unique_ptr destructor example #include <iostream> #include <memory>int main () {// user-defined deleterauto deleter = [](int*p){delete p;std::cout << "[deleter called]\n";};std::unique_ptr<int,decltype(deleter)> foo (new int,deleter);std::cout << "foo " << (foo?"is not":"is") << " empty\n";return 0; // [deleter called] }//输出 foo is not empty [deleter called]
unique_ptr 不能被拷贝,但可以被移动-例子:
因为不允许多个unique_ptr 指向同一块内存,因此不能进行拷贝,只能进行移动。所以unique_ptr也没有复制构造函数,不支持普通的拷贝和赋值操作。
std::unique_ptr<int>p1(new int(5));
std::unique_ptr<int>p2=p1;// 编译会出错
std::unique_ptr<int>p3=std::move(p1);// 转移所有权, 现在那块内存归p3所有, p1成为无效的针.
p3.reset();//释放内存.
p1.reset();//无效
shared_ptr
我们提到的智能指针,很大程度上就是指的shared_ptr,shared_ptr也在实际应用中广泛使用,因为它的性质基本跟我们使用的普通指针一致。
它是一种引用计数智能指针,它允许多个指针共享同一块内存。每当创建一个std::shared_ptr指向同一个对象时,引用计数会增加。当释放一个指向该对象的std::shared_ptr时,引用计数会减少 1 个,当引用计数为零时,指向的内存会被自动释放。
显然,share_ptr可以进行赋值拷贝。
创建shared_ptr
shared_ptr<int> p = make_shared<int>(1);
shared_ptr<int> p2(p);
shared_ptr<int> p3 = p;
make_shared
make_shared 是 c++11 加入标准库的,专门用于生成独占型指针 std::shared_ptr 的模板函数。使用示例:
int main()
{// 1.shared_ptr<string> p1(new string("66888"));cout << *p1 << endl;// 2.shared_ptr<string> p2 = make_shared<string>("888888");cout << *p2 << endl;return 0;
}
我们更推荐使用 make_shared 创建 shared_ptr,性能和安全性更高。unique_ptr也同理,建议使用make_unique来创建。详细原因可参考:
C++ 学习系列 -- 智能指针 make_shared 与 make_unique_make_unique和make_shared-CSDN博客
通过智能指针的 get() 成员函数获得 其封装的普通指针:
void func() {shared_ptr<int> p = make_shared<int>(1);int *p2 = p.get();cout<<*p2<<endl;
}
博文参考来源:
C++RAII机制:C++RAII机制-CSDN博客
智能指针:
c++智能指针详解_bitcarmanlee的博客-CSDN博客
智能指针 unique_ptr 详解-CSDN博客
cpp中易混淆总结(面经总结)-CSDN博客
C++ 学习系列 -- 智能指针 make_shared 与 make_unique_make_unique和make_shared-CSDN博客