c++之堆的使用

来源:互联网 发布:mac 隐藏桌面磁盘 编辑:程序博客网 时间:2024/04/29 00:27

定义一个指针指向堆中的空间,指针所指向的空间是匿名的,只能通过该指针才能进行访问。用new关键字申请的匿名空间,必须用delete关键字进行删除。

一、在堆中创建内存

堆中用new创建的内存在程序结束时不会自动释放,通过指针来访问堆中的数据,程序结束时指针被自动释放,所以导致堆中的内存空间无法使用,也无法找到。这就是内存泄漏;所以尽量使用delete手动释放堆空间

#include<iostream>using namespace std;int main(){   int *p;   p=new int;///堆,p指向创建的堆内存区域,大小与类型有关   *p=16;   cout<<"*p:"<<*p<<endl;   cout<<"p:"<<p<<endl;   delete p;   cout<<"释放堆中空间后*p的值:"<<*p<<endl;   cout<<"重新定义指针p的指向"<<endl;   p=new int;   *p=8;   cout<<"*p"<<*p<<endl;   cout<<"p:"<<p<<endl;   return 0;}

堆中指针只能通过delete释放,并且这种释放只是释放了指针先前指向的那段内存空间,使得这段内存空间可以用来存放其他值。指针并没有被删除,这时的指针是一个指向不确定的指针,使用指针之后要将其置0,表示为空指针,此时所指向的地址是0,该地址不能内容不可访问。在栈中使用指针则不必考虑这个问题,因为栈中的指针会被系统自动释放,只有用new时才使用delete。

在如下例子中,当指针p所指向的堆中内存被delete后,指针p保存的内存地址并没有改变,但是被释放的那段内存被系统用来存放了其他值;新定义的指针p1恰巧指向指针p释放的空间。但当改变指针p指向的值时,指针p1所指向的值也随之改变。说明指针p还是能对先前释放的内存进行操作。因此,在delelte一段内存之后,最好不要再使用它,或者将此指针赋值为0。

#include<iostream>using namespace std;int main(){    int *p=new int;    *p=999;    cout<<"删除p之前指向的内存地址为:"<<p<<ends<<"*p的值为:"<<*p<<endl;    delete p;    cout<<"删除p之后指向的内存地址为:"<<p<<ends<<"*p的值为:"<<*p<<endl;    long *p1=new long;    *p1=99999;    cout<<"p1指向的内存地址为:"<<p1<<ends<<"*p1的值为:"<<*p1<<endl;    *p=23;    delete p;    cout<<"p所指向的值改变之后,此时p1的值为:"<<*p1<<endl;    cout<<"p本身的地址为:"<<&p<<endl;    cout<<"p1本身的地址为:"<<&p1<<endl;    return 0;}

二、在堆中创建对象

1.未使用delete释放堆中对象

class A{public:    A(){cout<<"构造函数执行中"<<endl;x=999;}    ~A(){cout<<"析构函数执行中"<<endl;}private:    int x;};int main(){    A *p=new A;//在堆中创建匿名对象,只能通过指针访问该对象。并且在程序结束时,需要用delete手动释放堆中的对象。    return 0;//程序并没有调用析构函数}

2.使用delete释放堆中对象

class A{public:    A(){cout<<"构造函数执行中"<<endl;x=999;}    ~A(){cout<<"析构函数执行中"<<endl;}private:    int x;};int main(){    A *p=new A;    delete p;//delete调用析构函数,释放堆中的对象    return 0;}

3.访问堆中的数据:堆采用匿名方式保存数据,只有通过指针才可以访问到数据,安全性高。堆中的空间不会自动释放,只有通过程序员来释放,属于动态内存;堆与栈不同,栈是系统自动分配内存,堆需要程序员通过new关键字来分配内存,栈是一段连续的内存空间,它的大小最多2M,而堆的内存空间不连续,由链表链接起来

class A{public:    A(){cout<<"构造函数执行中"<<endl;x=999;}    ~A(){cout<<"析构函数执行中"<<endl;}    int getx(){return x;}private:    int x;};int main(){    A *p=new A;    cout<<(*p).getx()<<endl;    delete p;    return 0;}

4.在构造函数中开辟堆空间:在类的数据成员中,定义了一个指针类型的数据成员,并在构造函数中为指针成员申请堆中的空间。在程序结束时,系统自动调用析构函数析构对象,但是对象的数据成员在堆中的空间并未被释放,所以要在析构函数中加上delete i,这样才能在析构对象的时候释放指针i指向的堆中空间。

class A{public:    A(){cout<<"构造函数执行中"<<endl;i=new int(999);}    ~A(){cout<<"析构函数执行中"<<endl;delete i;}    int getx(){return *i;}private:    int *i;};int main(){    A *p=new A;    cout<<(*p).getx()<<endl;    delete p;    return 0;}
三、使用指针常见的错误

前面说过,在堆中删除一个指针后,一定要将该指针设置为空指针,这是因为删除该指针只会释放它所指向的内存空间,不会删除指针,因此这个指针还存在,并且仍然指向原来的空间。这时如果再次尝试使用该指针,就会出现错误。

在下例中,虽然指针p的对象被释放,但是p中保存的地址不变,使用p还是能改变该地址中对象的数据成员。

#include<iostream>using namespace std;class A{public:    A(){i=100;cout<<"构造函数执行中..."<<endl;}    ~A(){cout<<"析构函数执行中..."<<endl;}    int get(){return i;}    void set(int x){i=x;}private:    int i;};int main(){    A *p=new A;    cout<<(*p).get()<<endl;    cout<<p<<endl;    delete p;    cout<<"删除P之后:"<<endl;    cout<<(*p).get()<<endl;    cout<<p<<endl;    A *a=new A; //a与p指向同一个内存地址    cout<<(*a).get()<<endl;    cout<<a<<endl;    (*p).set(200);//p改变了对象的数据成员    cout<<(*a).get();    return 0;}
其实用delete命令处理某个指针,说是把那个指针删除了是不正确的。delete命令指示释放了那个指针原本所指的那部分内存而已。被delete后的指针p的值(地址值)并非就是NULL,而是随机值。也就是被delete后,如果不再加上一句p=NULL,p就成了“野指针”,在内存里乱指一通。如果在定义p的那个函数在delete了p后,没再调用p,就没什么问题,在这个函数结束后,p就会跟其它变量一样被消除。但若在那个函数里delete了p后,又没再给p赋值(地址值),再次调用p就危险了,因为这时p在内存里乱指,有可能指到一些重要地址,随时可能系统崩溃。用delete删除一个指针后,这个指针还可以再次赋值使用。

#include <iostream>int main(){    int *p = new int;    *p = 1;    std::cout<<"p's adress is "<<p<<std::endl;    delete p;    if(p == NULL)    {        std::cout<<"p has changed"<<std::endl;    }    else    {        std::cout<<"p hasn't changed"<<std::endl;    }    std::cout<<"p's adress is "<<p<<std::endl;    return 0;}









0 0
原创粉丝点击