您的位置:首页 > 编程语言 > C语言/C++

c++之堆的使用

2015-11-13 15:50 218 查看
定义一个指针指向堆中的空间,指针所指向的空间是匿名的,只能通过该指针才能进行访问。用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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: