C++智能指针
C++ 智能指针面试点
1. C++智能指针有哪些?
C++主要有三种智能指针(位于头文件memory中)
std::unique_ptr- 不能被复制,只能移动(
std::move)。 - 适用于独占资源管理(如文件、网络连接)。
- 用
std::make_unique创建(C++14)
- 不能被复制,只能移动(
std::shared_ptr- 采用引用计数,多个
shared_ptr可共享同一个对象,最后一个销毁时释放资源。 - 存在循环引用风险,可配个
std::weak_ptr解决。
- 采用引用计数,多个
std::weak_ptr- 依赖
shared_ptr,不会增加引用计数。 - 用于解决
shared_ptr循环引用问题。 - 通过
lock()获取shared_ptr,判断对象是否仍然有效。
- 依赖
2. std::shared_ptr原理是什么?实现shared_ptr
分析:
- 每个
shared_ptr实例指向同一个对象,并共享一个计数器。 - 当创建
shared_ptr时,计数器初始值为1,拷贝现有shared_ptr时,计数器会+1。 - 当
shared_ptr被销毁(例如通过析构函数)或重置(通过reset方法)时,计数器会-1。 - 当计数器为0时,对象被自动删除。
代码
#include <iostream>
using namespace std;
template <typename T>
class shared_count {
private:
T* m_ptr; //指向管理对象
int m_count; //引用计数
public:
shared_count(T* p = nullptr) : m_ptr(p), m_count(1) {}
shared_count(const shared_count&) = delete; //禁止拷贝构造
shared_count& operator=(const shared_count&) = delete; //禁止拷贝赋值
~shared_count() { delete m_ptr; }
//增加引用计数
void increment() {
m_count++;
}
//减少引用计数
void decrement() {
if (--m_count == 0) {
delete this;
}
}
//返回引用计数的数值 需要加上const
int use_count() const {
return m_count;
}
};
template <typename T>
class shared_ptr {
private:
T* m_ptr;
shared_count<T>* m_count;
public:
shared_ptr(T* p = nullptr) : m_ptr(p) {
if (p) {
m_count = new shared_count(p);
}
/*
else // 没意义的操作,主要有两点
p = nullptr; // 1.假设进去else分支,此时p本身就为nullptr,p = nullptr操作无意义
*/ // 2.p是形参,修改p的值并不会影响调用方传过来的原始指针
}
~shared_ptr() {
if (m_count) {
m_count->decrement();
}
}
//拷贝构造时,引用+1
shared_ptr(const shared_ptr& rhs) : m_ptr(rhs.m_ptr), m_count(rhs.m_count) {
if (rhs.m_ptr) {
m_count->increment();
}
}
//移动构造时,转移资源所有权
shared_ptr(shared_ptr&& rhs) : m_ptr(rhs.m_ptr), m_count(rhs.m_count) {
rhs.m_ptr = nullptr;
rhs.m_count = nullptr;
}
/*
reset的用法一定要记住
细节处理reset参数和不带参数
*/
void reset(T* ptr = nullptr) {
if (ptr != m_ptr) {
if (m_count) {
m_count->decrement();
}
}
m_ptr = ptr;
if (m_ptr) {
m_count = new shared_count(ptr);
}
}
T& operator*() const {
return *m_ptr;
}
T* operator->() const {
return m_ptr;
}
T* get() const {
return m_ptr;
}
int use_count() const {
return m_count->use_count();
}
};
int main() {
shared_ptr<int> ptr1(new int(10));
cout << "value: " << *ptr1 << ", count: " << ptr1.use_count() << endl;
shared_ptr<int> ptr2 = ptr1;
cout << "value: " << *ptr2 << ", count: " << ptr2.use_count() << endl;
shared_ptr<int> ptr3 = move(ptr2);
cout << "value: " << *ptr3 << ", count: " << ptr3.use_count() << endl;
ptr3.reset();
cout << "value: " << *ptr1 << ", count: " << ptr1.use_count() << endl;
cout << "RawPtr: " << ptr1.get() << endl;
return 0;
}
make_shared相比new + shared_ptr的好处?
1. 内存分配效率
1)new + shared_ptr的方式创建智能指针时,内存分配分为两步
new分配对象的内存shared_ptr构造函数分配的控制块内存
2)make_shared的内存分配只有一步
- 将对象和控制块的存储空间合并为一次进行内存分配
- 被管理的对象和内存时连续的,减少了内存分配的开销
所以从make_shared由于内存分配只有一步,此时对象和控制块位于同一片内存,有访问速度快的优势。
2. 异常安全
1)new + shared_ptr非异常安全,存在内存泄漏的风险
当函数参数使用new时,可能因参数求值顺序导致内存泄漏。例如:
func(std::shared_ptr<T>(new T), dowork());
由于函数参数的执行顺序是不确定的,有可能按照如下顺序执行new T成功,dowork()抛出异常,而shared_ptr尚未构造完成,此时最开始的new T分配的内存不会被释放。
2)make_shared的解决方案
func(std::make_shared<T>(), dowork);
make_shared在单步内完成对象的创建和智能指针的初始化,确保安全。
浙公网安备 33010602011771号