栈对象
隐含调用构造函数(程序中没有显示调用)
堆对象
隐含调用构造函数(程序中没有显示调用)
全局对象、静态全局对象
全局对象的构造先于main函数
已初始化的全局变量或静态全局对象存储于.data段中
未初始化的全局变量或静态全局对象存储于.bss段中
静态局部对象
已初始化的静态局部变量存储于.data段中
未初始化的静态局部变量存储于.bss段中
内存的分段方式
#include <iostream>
using namespace std;
class Test
{
public:
Test(int n) : n_(n)
{cout<<"Test "<<n_<<" ..."<<endl;}
~Test()
{cout<<"~Test "<<n_<<" ..."<<endl;}
private:
int n_;
};
int n; // 未初始化的全局变量,初始值为0。n存储于.bss段中。(block started by symbol)
int n2 = 100; // 已初始化的全局变量,初始值为100。n2存储于.data段中。
Test g(100); // 全局对象的构造先于main函数
static Test g2(200);// 静态全局对象的构造也会先于main函数
int main(void)
{
cout<<"Entering main ..."<<endl;
Test t(10); // 栈上创建对象,出了作用域,在其生存期结束的时候内存将会自动释放
{Test t(20); }
{Test* t3 = new Test(30); // 堆上创建对象,需要显式释放,对象的作用域与生存期不一定等同
delete t3;}
{
static int n3; // n3存储于.bss段中 (编译期初始化)
static int n4 = 100; // n4存储于.data段中 (编译期初始化)
static Test t4(333); // t4对象运行期初始化 .data段
}
cout<<"Exiting main ..."<<endl;
}
static及其作用域
一、面向过程设计中的static
1、静态全局变量
在全局变量之前,加上一个修饰关键字static,该变量就被定义成为一个静态的全局变量。
我们先举一个静态全局变量的例子,如下:
//Example 1
#include <iostream.h>
void fn();
static int n; //定义静态全局变量
void main()
void fn()
静态全局变量有以下特点:
该变量在全局数据区分配内存;
未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化);
静态全局变量或函数在声明它的整个文件之内都是可见的,而在文件之外是不可见的,不会暴露给别的编译单元 ;
静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量。
一般程序的由new产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静 态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。细心的读者可能会发现,Example 1中的代码中将
static int n; //定义静态全局变量
改为
int n; //定义全局变量
程序照样正常运行。的确,定义全局变量可以实现变量在文件中的共享,但定义静态全局变量还有以下好处:
静态全局变量只能够在当前文件当中是可见的,但是不能被其它文件所使用;因此,其它文件中可以定义相同名字的变量,不会发生冲突;
您可以将上述示例代码改为如下:/Example 2 //File1 #include <iostream.h> void fn(); static int n; //定义静态全局变量 void main()
//File2 #include <iostream.h> extern int n; void fn()
编译并运行Example 2,您就会发现上述代码可以分别通过编译,但运行时出现错误。
试着将
static int n; //定义静态全局变量
改为
int n; //定义全局变量
再次编译运行程序,就可以体会到全局变量和静态全局变量之间的区别。