c++之堆的使用
2015-11-13 15:50
218 查看
定义一个指针指向堆中的空间,指针所指向的空间是匿名的,只能通过该指针才能进行访问。用new关键字申请的匿名空间,必须用delete关键字进行删除。
一、在堆中创建内存
堆中用new创建的内存在程序结束时不会自动释放,通过指针来访问堆中的数据,程序结束时指针被自动释放,所以导致堆中的内存空间无法使用,也无法找到。这就是内存泄漏;所以尽量使用delete手动释放堆空间
堆中指针只能通过delete释放,并且这种释放只是释放了指针先前指向的那段内存空间,使得这段内存空间可以用来存放其他值。指针并没有被删除,这时的指针是一个指向不确定的指针,使用指针之后要将其置0,表示为空指针,此时所指向的地址是0,该地址不能内容不可访问。在栈中使用指针则不必考虑这个问题,因为栈中的指针会被系统自动释放,只有用new时才使用delete。
在如下例子中,当指针p所指向的堆中内存被delete后,指针p保存的内存地址并没有改变,但是被释放的那段内存被系统用来存放了其他值;新定义的指针p1恰巧指向指针p释放的空间。但当改变指针p指向的值时,指针p1所指向的值也随之改变。说明指针p还是能对先前释放的内存进行操作。因此,在delelte一段内存之后,最好不要再使用它,或者将此指针赋值为0。
二、在堆中创建对象
1.未使用delete释放堆中对象
2.使用delete释放堆中对象
3.访问堆中的数据:堆采用匿名方式保存数据,只有通过指针才可以访问到数据,安全性高。堆中的空间不会自动释放,只有通过程序员来释放,属于动态内存;堆与栈不同,栈是系统自动分配内存,堆需要程序员通过new关键字来分配内存,栈是一段连续的内存空间,它的大小最多2M,而堆的内存空间不连续,由链表链接起来
4.在构造函数中开辟堆空间:在类的数据成员中,定义了一个指针类型的数据成员,并在构造函数中为指针成员申请堆中的空间。在程序结束时,系统自动调用析构函数析构对象,但是对象的数据成员在堆中的空间并未被释放,所以要在析构函数中加上delete i,这样才能在析构对象的时候释放指针i指向的堆中空间。
前面说过,在堆中删除一个指针后,一定要将该指针设置为空指针,这是因为删除该指针只会释放它所指向的内存空间,不会删除指针,因此这个指针还存在,并且仍然指向原来的空间。这时如果再次尝试使用该指针,就会出现错误。
在下例中,虽然指针p的对象被释放,但是p中保存的地址不变,使用p还是能改变该地址中对象的数据成员。
一、在堆中创建内存
堆中用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; }
相关文章推荐
- C++ Primer 学习笔记——表达式
- C++基础::函数、类、类型所在的头文件 && 接口的介绍
- JAVA的泛型和C++的模板的区别与联系
- TinyXML:一个优秀的C++ XML解析器
- C语言中static的作用及C语言中使用静态函数有何好处
- C++ 之 Excel文件读写 之 简便方法
- C语言
- [LeetCode]Linked List Cycle II
- c语言之指针与数组
- 静态map成员的一种替代方法
- C语言 memcpy memmove
- C++整型和字符串转换
- VC++ 获取硬盘序列号源码(XP适用)
- c语言命令行参数
- 蓝桥杯练手之高斯日记
- 《C++ Primer》学习笔记共享
- JAVA和C/C++的区别
- JAVA如何调用C/C++方法
- 【C语言】 缓冲区问题 循环使用getchar scanf会受到换行符'\n'的干扰
- c++类的使用总结