【C++11 面试】深剖智能指针(上篇) , 10+代码让你彻底理解 , 面试重要重要重要 !
本篇文章是 C++部分非常非常非常重要的一篇关于智能指针的讲解 , 同时内容也是面试考察的重点~真正的重点在下篇 ~ 因为笔者要控制篇幅 , 所以只好分开了.本节详细代码库 :点击可跳转本节文档 :●智能指针是C++中用于管理动态分配内存的高级工具,它通过封装普通指针并利用RAII(Resource Acquisition Is Initialization,资源获取即初始化)技术,确保在对象生命
文章目录
前言
本篇文章是 C++部分非常非常非常重要的一篇关于智能指针的讲解 , 同时内容也是面试考察的重点 ~
真正的重点在下篇 ~ 因为笔者要控制篇幅 , 所以只好分开了.
本节详细代码库 : 点击可跳转
本节文档 : CPlusPlus.com
一、什么是智能指针 ?
● 介绍
智能指针是C++中用于管理动态分配内存的高级工具,它通过封装普通指针并利用RAII(Resource Acquisition Is Initialization,资源获取即初始化)技术,确保在对象生命周期结束时自动释放内存,从而避免内存泄漏和其他资源管理错误。
智能指针最重要的特点是可以帮我们管理资源的释放 , 不需要我们管理了 .
同时智能指针可以像指针一样 , 拥有普通指针的功能 .
● 为什么要有智能指针呢 ?
C++中也有指针的存在 ,为什么还要设计智能指针出来呢 ? 这里就与异常部分有关了 .
在异常机制中 , throw 会抛出异常 ,然而抛出异常后 , throw 后面的代码就不会去执行了 , 就会跳转到 try - catch 语句去捕获异常信息 .
struct Ptr
{
Ptr() {}
~Ptr() {cout << "~Ptr()" << endl;}
int* _ptr = nullptr;
};
//演示代码
double Division(const int a , const int b)
{
Ptr* ptr1 = new Ptr();
Ptr* ptr2 = new Ptr();
// a / b
if (b == 0)
throw(string("Division Zero Error !"));
//调用 Ptr 的析构函数
delete ptr1;
delete ptr2;
return (double)a / b;
}
int main()
{
int a = 0, b = 0;
cin >> a >> b;
try
{
cout << Division(a, b) << endl;;
}
catch (const string& str)
{
cout << str << endl;
}
catch (...)
{
cout << "Unknown Exception !" << endl;
}
return 0;
}
那么针对这样的情况 , 想要内存不泄露 , 那我们还需要重新抛出异常来解决 , 这是不方便的 , 所以这里就提出了智能指针 , 很香 ~ ~ .
所以智能指针能很好的解决资源泄漏的问题 .
二、智能指针初见
前面也提到了 , 智能指针就是对普通指针的封装 , 可以像普通指针一样 , 准确来说 , 兼容指针功能 .
● RAII 思想
- 什么是 RAII ? (重要重要)
RAII 即 : Resource Acquisition Is Initialization . 中文 : 资源获得立即初始化 .
本质 : 把资源拿来初始化 , 即 ; 对象得到资源 , 当对象生命周期结束时 , 进行析构把资源带走 , 这样就有效避免资源泄漏了 .
● 为什么智能指针可以有效避免资源泄漏呢 ?
这里具体分析一下 :
- 实现一个简易的智能指针
//演示代码
template <class T>
class smart_ptr
{
public:
//RAII 思想
smart_ptr(T* ptr)
:_ptr(ptr)
{}
~smart_ptr()
{
delete _ptr;
_ptr = nullptr;
cout << "~smart_ptr()" << endl;
};
//还要像普通指针一样 , 重载 * ->
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
//演示代码
double Division(const int a, const int b)
{
//这里 new 的底层是 operator new , 底层是 malloc , 返回是还是指针
//所以可以用智能指针接受
smart_ptr<int> ptr1(new int(1));
smart_ptr<int> ptr2(new int(2));
// a / b
if (b == 0)
throw(string("Division Zero Error !"));
return (double)a / b;
}
int main()
{
int a = 0, b = 0;
cin >> a >> b;
try
{
cout << Division(a, b) << endl;;
}
catch (const string& str)
{
cout << str << endl;
}
catch (...)
{
cout << "Unknown Exception !" << endl;
}
return 0;
}
以上就可以体现出智能指针的用处了 , 很好用 , 不需要我们去手动去释放了 , 交给对象自动管理 .
● 总结智能指针的设计思路
三、深剖智能指针
● C++标准库中的智能指针 .
点击可跳转 !
● 智能指针拷贝问题
智能指针的设计思想 RAII , 这个思想是让智能指针去代管资源 , 从而当对象生命周期结束时 , 去释放这块资源 , 但这样就没有问题吗 ?
所以就出来了标准库中的各种智能指针 , 这些指针的历史笔者就不探讨了 , 有兴趣的可以自行查看 !
● 各类智能指针介绍
1. 为什么会有各类智能指针 ? (重要)
以上笔者也展示了标准库中的各类指针指针 , 那么为什么会有这么多呢 ? 这同时也是常考题目
auto_ptr / shared_ptr / unique_ptr 它们的本质都是为了解决智能指针拷贝的问题 , 它们底层解决拷贝问题的思路和方式不同 , 所以会有这几个智能指针 .
2. auto_ptr
直接前提结论 : 不使用它 ! 不使用它 ! 不使用它 !
- 语法
- 重要理解
可以发现 auto_ptr 的设计很危险 , 当管理权转移了以后 , 对象被悬空了 , 我们可能不知道再去访问程序就报错了 !
2. unique_ptr (重要重要)
这个智能指针是 C++11 提出的 , 那么这个就很有必要了 .
- 语法
- 重要重要重要理解
需要详细代码看代码仓 ~~~~~~~~~~
#include <memory>
struct Ptr
{
//强制默认构造
Ptr() = default;
Ptr(int* ptr)
:_ptr(ptr)
{}
~Ptr() { delete _ptr; _ptr = nullptr; cout << "~Ptr()" << endl; }
int* _ptr = nullptr;
int _a = 1;
};
// unique_ptr 和 auto_ptr
void Test01()
{
//不支持这样写 , 因为增加了 explicit 修饰 , 即不支持隐式类型转换
//auto_ptr<Ptr> ptr = new Ptr(); // err
int* ptr = new int(1);
auto_ptr<Ptr> ptr1(new Ptr(ptr)); // ok 构造
auto_ptr<Ptr> ptr2(ptr1); // ok 拷贝构造
//这里可以通过调试看到 ptr1 被悬空了 , 很危险 !!!!
//增加了 explicit 修饰, 即不支持隐式类型转换
//unique_ptr<Ptr> ptr3 = new Ptr(); // err
//ptr3 和 arr 是同一块资源
unique_ptr<Ptr> ptr3(new Ptr(ptr)); // ok
//不支持拷贝
//unique_ptr<Ptr> ptr4(ptr3); // err
}
所以 , 当我们不需要拷贝时 , 用 unique_ptr 就很香了 ~
3. shared_ptr (极其极其重要)
- 语法
- 使用
#include <memory>
struct Ptr
{
//强制默认构造
Ptr() = default;
Ptr(int* ptr)
:_ptr(ptr)
{}
~Ptr()
{
delete _ptr;
_ptr = nullptr;
cout << "~Ptr()" << endl;
}
int* _ptr = nullptr;
};
//shared_ptr 使用
void Test02()
{
//template <class U> explicit shared_ptr (U* p);
//不支持隐式类型转换
//shared_ptr<Ptr> ptr1 = new Ptr(); // err
//RAII
int* ptr = new int(1);
shared_ptr<Ptr> ptr1(new Ptr(ptr)); // ok
//支持拷贝
shared_ptr<Ptr> ptr2(ptr1); // ok
//调试可看出 , ptr / ptr1 / ptr2 都指向的同一块资源 , 并且只析构了一次
}
四、模拟底层实现(面试考)
模拟实现部分是面试重要考察点 , 需要我们手撕代码 ~~~~~~~~
本节笔者放入下篇讲解 , 因为这里还涉及的内容很多 , 为了控制篇幅 , 这里希望学者移至下篇敬请查看 !!!
五、智能指针基础总结
总结
以上是对智能指针部分的介绍 , 重点还在下篇 , 请移步 !!
更多推荐
所有评论(0)