- 对于一个完整的程序,在内存中的分布情况如下图:
- 栈区: 由编译器自动分配释放,像局部变量,函数参数,都是在栈区。会随着作用于退出而释放空间。栈由系统自动分配,速度较快,在windows下栈是向低地址扩展的数据结构,是一块连续的内存区域,大小是2MB。
- 堆区:程序员分配并释放的区域,像malloc(c),new(c++) 。堆需要程序员自己申请,并指明大小,速度比较慢。在C中用malloc,C++中用new。另外,堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机的虚拟内存。因此堆空间获取和使用比较灵活,可用空间较大。
- 全局数据区(静态区):全局变量和静态变量存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束释放。
- 常量区:存放常量(程序在运行的期间不能够被改变的量,例如: 10,字符串常量”abcde”, 数组的名字等)
- 代码区
-
- Static
- 局部静态变量:虽然在函数内部,但是储存于全局数据区,只初始化一次,即使在不同函数被调用的过程中,仍然持续存在,不会被被破坏又被建立,
- 该变量在全局数据区分配内存(局部变量在栈区分配内存);
- 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化(局部变量每次函数调用都会被初始化);
- 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0(局部变量不会被初始化);
- 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,也就是不能在函数体外面使用它(局部变量在栈区,在函数结束后立即释放内存);
int f(int n){ static int i=1; if(n>=5) return n; n=n+i; i++; return f(n); } //输出7
- new
- 自动存储,也叫做自动变量,比如int num = 10; 这个num就属于自动变量。所谓自动,代表它会自动申请内存,也会自动释放内存,自动变量是保存在栈里的(后进先出)。 如果大家觉得很难理解,那么,换一个名称——局部变量。局部变量在离开函数,或者离开它所属的代码块之后,就会被释放。而Value v = Value(100); 、int num = 10; 这些都是局部变量,一旦离开函数或者离开它的作用域,就会被释放。比如把int num; 作为成员变量,那么,在这个类被释放的时候,num变量也会被释放。这就是为什么我们在创建了这么多int、float等基本类型的变量之后,不需要去释放它们。因为它们是自动被释放的。
- 动态存储:
- 自动变量有很大的好处,那就是不需要我们去管内存方面的事情,但是,有时候我们不希望有这样的自动释放内存。我们希望自己去控制什么时候释放对象,这时候就要用到new了。
- 我们都知道,new了之后,如果不调用相应delete的话,申请到的内存空间是永远都不会被释放的。这就是动态存储了,我们自己来申请内存,自己来释放内存。
- 当然,内存泄露的罪魁祸首之一也正是new~!
- 因为正常人都会有疏忽的时候,并且当程序足够庞大、逻辑足够复杂的时候,有些地方调用了new,却疏忽了delete是再正常不过了。
- new的特性
- new创建类对象需要指针接收,一处初始化,多处使用
- new创建类对象使用完需delete销毁
- new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
- new对象指针用途广泛,比如作为函数返回值、函数参数等
- 频繁调用场合并不适合new,就像new申请和释放内存一样
- 默认参数:
- 方便提供不同的解决方式
- 如果为一个参数提供了默认参数,那么这个参数右边的所有参数必须添加默认值。
- Reference
- 引用&必须指定一个对象,不能像pointer一样为空
- 对于reference,在函数中设定了一个默认值,那么这个默认值右边的所有值必须设定默认值。
- 对于reference,只能制定一次,不在函数声明处,则在函数定义处。一般放在声明处,便于阅读代码。
- inline函数的作用
- 在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
- 为解决这个问题,引入一个内联函数inline。
- 对于简单的,内部没有复杂的while,for循环等结构,本身不是递归函数的函数,可以声明为inline
- inline函数仅仅是一个对编译器的建议,所以最后能否真正内联,it depends,计算机如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联。
-
类中的成员函数与inline:
定义在类中的成员函数缺省都是内联的,如果在类定义时就在类内给出函数定义,那当然最好。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上inline,否则就认为不是内联的。
-
声明跟定义要一致:如果在每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则,将会引起未定义的行为。如果不是每个文件里的定义都一样,那么,编译器展开的是哪一个,那要看具体的编译器而定。所以,最好将内联函数定义放在头文件中。
-
关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。
-
以下情况不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。 -
本部分引用自https://www.cnblogs.com/fnlingnzb-learner/p/6423917.html,十分感谢