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>

【参考】

C++11:智能指针_智能指针c++11-CSDN博客

 图解C语言指针变量 - 知乎 (zhihu.com)

return new 内存泄漏 - 夕西行 - 博客园 (cnblogs.com)

posted @ 2023-11-23 20:18  夕西行  阅读(433)  评论(0编辑  收藏  举报