此文针对FishC大佬的《C++快速入门》第三十三讲—动态内存管理,本文是对其的笔记整理。
到目前为止,所有的示例程序在完成它的任务时所使用的内存空间都是固定不变的。
这个固定不变的内存空间其实是在编写程序的时候就可以知道和确定的(一般以变量的形式)。这些程序都不能在程序运行期间动态增加或减少内存空间。
前言
在很多时候,需要存储的数据量到底有多大,事先往往是个未知数,要想处理好这类情况,就需要在C++程序里使用动态内存。
动态内存支持程序员创建和使用种种能够根据具体需要扩大和缩小的数据结构,它们只受限于计算机的硬件内存总量和系统特殊约束。
静态内存
静态内存就是我们此前一直在使用的东西:变量(包括指针变量)、固定长度的数组、某给定类的对象。我们可以在程序代码里通过它们的名字或者地址来访问和使用它们。
使用静态内存的最大弊端是:你不得不在编写程序时为有关变量分配一块尽可能大的内存(以防不能存放数据)。一旦程序开始运行,不管实际情况如何,那个变量都将占用那么多的内存,没有任何办法能够改变静态内存的大小。
动态内存
动态内存是由一些没有名字、只有地址的内存块构成,那些内存块是在程序运行期间动态分配的。
它们来自一个由标准C++库为你管理的“大池子”(装B术语称之为“内存池”)
从内存池申请一些内存需要用new语句,它将根据你提供的数据类型分配一块大小适当的内存。你不必担心内存块的尺寸问题,编译器能够记住每一种数据类型的单位长度并迅速计算出需要分配多少个字节。
如果有足够的可用内存能满足你的申请,new语句将返回新分配的地址块的起始地址。
如果没有足够的可用内存空间呢???
——那么new语句将抛出std::bad_alloc异常!!
主要用完内存块后,应该用delete语句把它还给内存池。另外作为一种附加的保险措施,在释放了内存块之后还应该把之关联的指针设置为NULL。
图说编程:int *i = new int;
假设 左边是堆空间,右边是栈空间。
栈空间声明了一个指针变量i,存放的是一个地址,地址从哪来呢,由new,new出了一个int内存块,并返回它的初始地址也就是4并存放进去。
再来看看delete i;
指针变量i并没有发生改变,i是一个栈里声明的局部变量,并没有释放它,地址还是有的,只是把这个地址的内容释放掉了。所以为了保险起见,令i = NULL;
NULL指针
有一个特殊的地址值叫做NULL指针。当把一个指针变量设置为NULL时,它的含义是那个指针将不再指向任何东西:
int *x;
x = NULL;//x这时候啥也不指向
我们无法通过一个被设置为NULL的指针去访问数据。事实上,试图对一个NULL指针进行解引用将在运行时被检测到并导致程序中止执行。
所以在用delete释放内存后,指针会保留一个毫无意义的地址,我们要将指针变量赋值给NULL。
Pay Attention:
- 静态内存这个术语与C++保留字static没有任何关系。静态内存意思是指内存块的长度在程序编译时被设定为一个固定的值,而这个值在程序运行时是无法改变的。
- new语句返回的内存块很可能充满“垃圾”数据,所以我们通常先往里面写一些东西覆盖,再访问它们,或者在类直接写一个构造器来初始化。
- 在使用动态内存的时候,最重要的原则是每一条new语句都必须由一条与之配对的delete语句,没有配对的delete语句或者有两个配对的delete语句都属于变成漏洞。(尤其前者,将导致内存泄露)
为对象分配内存
为对象分配内存和为各种个基本数据类型(int,char,float)分配内存在做法上完全一样。
- 用new想内存池申请内存
- 用delete来释放内存
#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 << "。\n";
}
TechCompany::TechCompany(std::string theName, std::string product) : Company(theName)
{
this->product = product;
}
void TechCompany::printInfo()
{
std::cout << name << "公司大量生产了 " << product << "这款产品!\n";
}
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;
}
Pay Attention:
- 搞对象的时候,千万不要忘记把方法声明为虚方法,详情请参见【C++入门笔记】多态的实现原理中关于虚方法的笔记。
- 在重新使用某个指针之前千万不要忘记调用delete语句,如果不这么做,那个指针将得到一个新内存块的地址,如果不这样做,那个指针将得到一个新的内存块的地址,而程序将永远也无法释放原先那个内存块,因为它的地址已经被覆盖掉了。
- 请记住,delete语句只释放给定指针变量正指向的内存块,不影响这个指针。在执行delete语句之后,那个内存块被释放了,但指针变量还依然健在。
参考链接
《C++快速入门--小甲鱼》https://www.bilibili.com/video/av7595819/?p=16