Day03
- 今天补了一下前两天落下的,更新了一下day01 02
智能指针
- 智能指针是为了帮助解决动态内存分配的,它会帮助我们管理我们new出来的空间,避免内存泄漏
- 智能指针通过RAII(资源获取即初始化)机制解决这个问题:对象生命周期结束时自动释放资源
- 智能指针头文件:memory
- 智能指针分类:
std::unique_ptr
std::shared_ptr
std::weak_ptr
std::auto_ptr//c++11已经废用,这里不讨论
- std::unique_ptr:独占所有权指针,同一时刻只能有一个std::unique_ptr指向目标内存
int main() {
std::unique_ptr<int>p1(new int(50));//p1指向有50个元素的int数组
*p1 = 100;//数组首元素赋值100
std::cout << *p1 << std::endl;//输出100
//p1离开作用域自动释放内存,无需手动delete
std::unique_ptr<int>p2 = std::make_unique<int>(50);//c++14 更推荐的创建用法
return 0;
}
- std::unique_ptr独占所有权但是可以移动
std::unique_ptr<int> p3 = std::move(p1); // p1 变为空
- shared_ptr:共享所有权指针,支持多个shared_ptr指向同一目标内存
- 实际上我们可能需要多个指针同时指向同一内存,使用std::unique_ptr无法完成,所以我们需要shared_ptr
- 工作机理:shared_ptr有一个计数器,每次指向这片内存的shared_ptr多一个,计数器就加一,只有计数器归零的时候才会回收内存
class Resource {
public:
Resource() {
std::cout << "Resource acquired\n";
}
~Resource() {
std::cout << "Resource destroyed\n";
}
void use() {
std::cout << "Using resource\n";
}
};
void shared_ptr_demo() {
// 创建shared_ptr(推荐make_shared)
std::shared_ptr<Resource> s1 = std::make_shared<Resource>();
{
// 共享所有权
std::shared_ptr<Resource> s2 = s1;
std::cout << "Use count: " << s1.use_count() << std::endl; // 2
s2->use();
} // s2析构,引用计数减1
std::cout << "Use count: " << s1.use_count() << std::endl; // 1
// 自定义删除器
std::shared_ptr<Resource> s3(
new Resource(),
[](Resource * res) {
std::cout << "Custom deleter called\n";
delete res;
}
);
// 数组支持(C++17)
std::shared_ptr<Resource[]> arr(new Resource[3]);//s4 s5 s6
} // 资源自动释放
int main() {
shared_ptr_demo();
}
- 最终输出
Use count: 2//s1 s2两个
Using resource//s2->use
Use count: 1//s1 一个
Resource acquired//s3
Resource acquired//s4
Resource acquired//s5
Resource acquired//s6
Resource destroyed//s6
Resource destroyed//s5
Resource destroyed//s4
Custom deleter called//s3
Resource destroyed//s3
Resource destroyed//s1
-常见错误
int* raw = new int(42);
std::shared_ptr<int> p1(raw);
std::shared_ptr<int> p2(raw); // 双重释放!
//正确写法
std::shared_ptr<int> p1 = std::make_shared<int>(42);
auto p2 = p1; // 共享所有权
- 函数传递
// 正确传递方式:
void process_resource(std::shared_ptr<Resource> res); // 共享所有权
void use_resource(const Resource& res); // 只读访问
void modify_resource(Resource* res); // 写访问(需确保生命周期)
- std::weak_ptr:观察但是不拥有资源(不会增加std::shared_ptr的计数),核心用途是为了打破循环应用,一般和shared_ptr使用
- 循环引用问题
class B; // 前向声明
class A {
public:
std::shared_ptr<B> b_ptr;
~A() { std::cout << "A destroyed\n"; }
};
class B {
public:
std::shared_ptr<A> a_ptr;
~B() { std::cout << "B destroyed\n"; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b;
b->a_ptr = a;
// main 结束后,a 和 b 的引用计数都 > 0,无法释放
}
- 使用std::weak_ptr解决
class B; // 前向声明
class A {
public:
std::shared_ptr<B> b_ptr;
~A() {
std::cout << "A destroyed\n";
}
};
class B {
public:
std::weak_ptr<A> a_ptr;
~B() {
std::cout << "B destroyed\n";
}
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b;
b->a_ptr = a;
//可以正常释放
}
-
智能指针的底层
- shared_ptr控制块结构
struct ControlBlock { long shared_count; // 强引用计数 long weak_count; // 弱引用计数 Deleter deleter; // 自定义删除器 Allocator allocator; // 分配器 // 指向管理对象的指针 };
- new和make_shared的区别
特性 make_shared 直接使用new 内存分配 单次分配(对象+控制块) 两次分配(对象+控制块) 异常安全 是 可能泄漏 缓存友好 是(局部性原理) 否 弱引用延迟释放 控制块和对象一起释放 对象可单独释放 -
更多用法
- 自定义删除器
auto my_delete = [](Test* t){ //你自己的删除逻辑 delete t; }; std::shared_ptr<Test>Test_ptr(new Test(),my_delete);