C++内存分四大块
-
全局 主函数运行前使用,初始化
-
静态 变量第一次使用前,初始化
以上两块内存都会在程序结束后自动释放 -
堆区 由程序员管理,C++管理方法有new delete等关键字
-
栈区 由编译器管理,存放程序的局部变量和参数
因此我们需要关注堆区的内存管理。内存管理经常会碰到忘记释放造成的内存泄露。
在C++中引入了智能指针,有shared_ptr,unique_ptr和weak_ptr。
shared_ptr
工作中shared_ptr用得比较多,今天做个总结
使用智能指针,需要引入头文件,shared_ptr顾名思义是多个指针指向一块内存。
被管理对象有一个引用计数,这个计数记录在每个指针上,几个shared_ptr指向它,这个数字就是几,当没有任何shared_ptr指向它时,引用计数为0,这时,自动释放对象。
其功能在于当所有指针都释放(或是不再指向对象)的时候,自动释放对象
即,当一个所有指向这块内存的指针生命周期结束时,这块内存会被释放
智能指针的唯一作用,就是自动delete对象,即C++11的新特性,内存管理机制。
智能指针既然会自动delete对象,我们就不能再去手动delete对象了,否则,也会发生多次释放的问题
例:
#include <iostream>
#include <memory>
class Test {
public:
// 无参构造函数
Test();
// 有参数的构造函数
explicit Test(int a);
// 析构函数
~Test();
};
Test::Test() {
std::cout << "无参构造函数" << std::endl;
}
Test::Test(int a) {
std::cout << "有参构造函数,a=" << a << std::endl;
}
Test::~Test() {
std::cout << "析构函数" << std::endl;
}
int main(int argc, const char * argv[]) {
auto p1 = new Test; // 划分堆空间
std::shared_ptr<Test> sp(p1); // 创建智能指针
std::cout << sp.use_count() << std::endl; // 打印引用计数
{
std::shared_ptr<Test> sp2(sp); // 创建另一个智能指针
std::cout << sp.use_count() << std::endl; // 打印引用计数
} // sp2生命周期结束,sp引用计数减1
std::cout << sp.use_count() << std::endl; // 打印引用计数
return 0;
执行结果
无参构造函数
1
2
1
析构函数
make_shared的引入
一个坑
int main(int argc, const char * argv[]) {
auto p1 = new Test; // 划分堆空间
std::shared_ptr<Test> sp(p1); // 创建智能指针
std::shared_ptr<Test> sp2(p1); // 创建另一个智能指针
return 0;
}
这段程序会抛出异常 double free detected
new关键字返回的是对应的指针类型。
此处用了两个智能指针管理同一块内存,因为sp 和sp2不知道彼此的存在,所以也会重复释放。
同一个对象只能用同一套内存管理体系,如果它已经有智能指针了,那么再创建智能指针时,需要通过原来已有的指针创建,而不能重复用原始空间来创建。
STL库提供了make_shared函数,其原型为
template <typename T, typename ...Args>
std::shared_ptr<T> std::make_shared(Args && ...args)
官方鼓励用make_shared函数来创建对象,而不要手动去new,这样就可以防止我们去使用原始指针创建多个引用计数体系。
例
int main(int argc, const char * argv[]) {
auto sp = std::make_shared<int>(); // 分配堆空间,创建智能指针
auto sp2 = sp; // 创建另一个智能指针
return 0;
}
参考
https://blog.csdn.net/fl2011sx/article/details/103941346