c++本质:释放内存、new与delete、野指针
【释放内存】
本质:标识符放弃对该内存的占有权。即“释放内存”就是释放占有权。
若该内存是栈内存,当所有标识符都放弃,那么系统自动重获占有权。内存依然存在,地址、值都未改变。
若该内存是堆内存,当所有标识符都放弃,不delete,那么系统也无法拥有占有权。所以delete让系统重获占有权。内存依然存在,地址未变、值变为默认值。
下述案例,来理解“释放内存”
int* p = nullptr; { int x = 10; p = &x; } cout << *p << endl; //10
x已经超出作用域被释放,那么p为什么不是野指针,而正确输出了值?
VS打断点后Debug运行后,VS中调试——窗口——内存,查看地址和对应的值。
x超出作用域后,地址、值都完好无损。与我们理解的释放内存不一样啊,fuck~~~
当x超出作用域,x放弃占有权,但p依然占有。若p也超出作用域,放弃占有权,则系统重获占有权。
【new与delete】
本质:new在堆上申请内存,标识符对该堆内存具有占有权。delete交还占有权给系统。内存依然存在,值会变为默认值。
也就是说如果不delete,这块内存系统无权再安排给其他使用,浪费资源。
下述案例,来理解new与delete
int* p = nullptr; { int* x = new int(10); //堆内存 p = x; //delete x; //x = nullptr; } //delete p; //p = nullptr;
在作用域内,delete x或p都可以,但是不可以delete两次。
【野指针】
本质:系统拥有该内存占有权,依然指向该内存的指针就是野指针。
系统和野指针都可以给该内存赋值,所以这个值是不确定的。因此指针推荐置为nullptr。
放弃占有权,只要标识符不消亡依然可以赋值,只是不能再delete,也无法保证内存值的稳定性。
上述案例中,delete x后,p其实就是野指针。所以可以撰写如下
int* p = nullptr; { int* x = new int(10); p = x; //p指向该内存,并有占有权 delete x; //x和p都失去占有权,系统拥有占有权 x = nullptr; } //x标识符消亡 *p = 11; //给内存赋值,注意系统也可以安排给其他对象来赋值 p = nullptr; //p已经无法保证内存值的稳定性了,置为nullptr
借此,引出经典的野指针场景——容器内是指针
vector<int*> vDatas; int* pData1 = new int(1); int* pData2 = new int(2); vDatas.push_back(pData1); vDatas.push_back(pData2); for each (int* pData in vDatas) { delete pData; pData = nullptr; } pData1 = nullptr; pData2 = nullptr;
也引出经典的智能指针,超出作用域自动释放占有权。
多个指针指向同一内存的,使用shared_ptr。只能有一个指针指向该内存的,使用unique_ptr。
shared_ptr<int> sptr1 = make_shared<int>(); //shared_ptr<int> sptr1(new int); //推荐上述初始化方式 { shared_ptr<int> sptr2 = make_shared<int>(10); sptr1 = sptr2; }
注意引入头文件 #include <memory>
【参考】