到目前为止,以前章节的每一个示例程序在完成它的任务时所使用的内存空间都是固定不变的。这个固定不变的内存空间其不实是在编写程序时候就可以知道和确定(一般以变量的形式)。这些程序都不能再程序运行期间动态增加或减少内存空间。
你见过要求用户输入的文本必须不多不少包含多少个字符的程序吗?不可能吧?!
在很多时候,需要存储的数据量到底有多大在事先往往是一个未知数,要想处理好这类情况,就需要在C++程序里使用动态内存。
动态内存支持程序猿创建和使用种种能够根据具体需要扩大和缩小的数据结构,它们只受限于计算机的硬件内存总量和系统特殊约束。
接下来,我们将学到如何以这种灵活的方式与内存交道。
静态内存
静态内存就是我们此前一直在使用的东西:变量(包括指针变量)、固定长度的数组、某给定类的对象。我们可以在程序代码里通过它们的名字或者地址来访问和使用它们。
使用静态内存的最大弊端是,你不得不在编写程序时为有关变量分配一块尽可能大的内存(以防不够存放数据)。一旦程序开始运行,不管实际情况如何,那个变量都将占用那么多的内存,没有任何办法能改变静态内存的大小。
动态内存
动态内存由一些没有名字、只有地址的内存块构成,那些内存块是在程序运行期间动态分配的。
它们来自一个由标准C++库替你管理的“大池子"(装B术语称之为“内存池")从内存池申请一些内存需要用new语句,它将根据你提供的数据类型分配一块大小活当的内存。你不必担心内存块的已寸问题,编译器能够记住每一种数据类型的单位长度并汉速计算出需要分配多少个字节。
如果有足够的可用内存能满足你的申请,new 语句桨返回新分配地址块的起始地址。
如果没有足够的可用内存空间?
一那么new 语句将抛出std::bad_alloc异常!
注意在用完内存块之后,应该用delete语句把它还给内存池。另外作为一种附加的保险措施,在释放了内存块之后还应该把与之关联的括针设置为NULL。
图说编程:
int *i = new int;
delete i;
i = NULL;
NULL指针
有一个特殊的地址值叫做NULL指针。当把一个指针变量设置为NULL时,它的含义是那个指针将不再指向任何东西:
int *x; X=NULL;//x这时候啥都不指向
我们无法通过一个被设置为NULL的指针去访问数据。事实上,试图对一个NULL 指针进行解引用将在运行时被j检测到并将导致程序中止执行。所以在用 delete释放内存后,指针会保留一个毫云意义的地址,我们要将指针变量赋值为NULL。
pay attention
请注意,静态内存这个术语与C++保留字static 没有任何关系。静态内存意思是指内存块的长度在程序编译时被设定为一个固定的值,而这个值在程序运行时是无法改变的。
new 语句返回的内存块很可能充满“垃最“数据,所以我们通常先往里边写一些东西覆盖,再访问它们,或者在类直接写一个构浩器来初始化。
在使用动态内存时,最重要的原则是每一条 new 语句都必须有一条与之配对的delete 语句,没有配对的 delete语句或者有两个配对的 delete 语句都属于编程漏洞。(尤其前者,将导致内存泄漏)
为对象分配内存
为对象分配内存和为各种基本数据类型(int,char,floa...)分配内存在做法上完全一样。
- 用new向内存池申请内存
- 用 delete来释放内存
这个概念其实我们老早前就给大家骗示了:在虚函数那一讲中有木有?!
我们再用一个栗子给大家取固现固之前的知识:Example.cpp
#include<iostream> #include<string> class Company { public: Company(std::string theName); virtual void printInfo();//声明为虚函数 protected: std::string name; }; class TechCompany : public Company { public: TechCompany(std::string theName,std::string product); virtual void printInfo(); private: std::string product; } ; Company::Company(std::string theName) { name = theName; } void Company::printInfo() { std::cout<<"这个公司的名字叫:"<<name<<"。 "; } TechCompany::TechCompany(std::string theName,std::string product):Company(theName) { this->product = product; } void TechCompany::printInfo() { std::cout<<name<<"公司大量生产了"<<product<<"这款产品! "; } int main() { Company *company = new Company("APPLE"); company -> printInfo(); delete company; company = NULL; company = new TechCompany("APPLE","IPHONE"); company -> printInfo(); delete company; company = NULL; return 0; }
结果:
这个公司的名字叫:APPLE。 APPLE公司大量生产了IPHONE这款产品! 请按任意键继续. . .
pay attention
搞对象的时候,千万不要忘记把方法声明为虚方法,如仍有疑问请回顾虚方法那一节。
在重新使用某个指针之前千万不要忘记调用 delete语句,如果不这样做,那个指针将得到一个新内存块的地址,而程序将永远也无法释放原先那个内存块,因为它的地址已经被覆盖掉了。
请记住,delete 语句只释放给定括针变量正指向的存块,不影响这个指针。在执行 delete 语句之后那个内存块被释放了,但括针变量还依然健在哦。