内存分区
在C/C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。
堆和栈区别
1、管理方式不同;
对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
2、空间大小不同;
一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。
3、能否产生碎片不同;
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出。
4、生长方向不同;
对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
5、分配方式不同;
堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由malloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
6、分配效率不同;
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
全局变量和静态变量的区别
static变量指静态变量,不论是全局static变量还是局部static变量都放在全局/静态存储区,所以它的生命周期是从程序开始到程序结束。但static变量的作用域会有所不同。
局部变量
局部static变量的例子:
void test()
{
int m=3;
static int i=5;
}
局部变量m存放在栈中,当test函数结束,m将被销毁;
静态变量i存放于程序的全局变量区域,当函数test结束它并不随着出栈操作而被销毁,它的生存周期存在于程序的整个运行期;
然而m和i的作用域都仅存在于test函数中它们的定义之后,即test调用结束之后,m和i就不再可用,但是i仍存在于内存之中。静态局部变量在程序执行到该对象的声明处时被首次初始化,以后的函数调用不再初始化,下一次依据上一次结果值。
全局变量
全局static变量的例子:在文件A 中定义静态变量j
//fileA
#include<iostream.h>
void fn();
int n=3;
static int j=5;//定义静态全局变量
void main()
{
n=20;
cout<<n<<end;
fn();
}
void fn()
{
n++;
cout<<n<<endl;
}
全局变量和静态变量j都存放于程序的全局数据区域,它们的生存周期都是程序的整个运行期,未经初始化的静态全局变量会被程序自动初始化为0。
但是全局变量n的作用域为全局作用域,可以通过extern在其他文件中使用;
而静态变量j只能在文件A中使用,例如在文件B中:
//fileB
extern int n;
extern int j;
int a=n;
int b=j;//error
也就是说,在声明全局的static变量时,static没有改变它的生存周期,也即存储位置(因为全局变量本来就存储在全局数据域),而是将变量的作用域限制在当前文件中。
static用法
内部机制
静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。
静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声明中写成定义是错误的。
它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。
static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间。
static修饰函数
这个函数的只能在本文件中调用,不能被其他文件调用。其他文件可以定义相同名字的函数,不会发生冲突。
#include<iostream.h>
static void fn();
void main()
{
fn();
}
void fn()
{
int n=10;
cout<<n<<endl;
}
静态数据成员
class myclass
{
public:
myclass(int a,int b,int c);
void getsum();
private:
int a,b,c;
static int sum;//声明静态数据成员
}
int myclass::sum=0;//定义并初始化静态数据成员
myclass::myclass(int a,int b,int c)
{
this->a=a;
this->b=b;
this->c=c;
sum+=a+b+c;
}
void myclass::getsum()
{
cout<<"sum="<<sum<<endl;
}
void main()
{
myclass m(1,2,3);
m.getsum();
myclass n(4,5,6);
n.getsum();
m.getsum();
}
//输出: sum=6
//sum=21
//sum=21
1、对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;
2、静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。在Example 5中,语句int Myclass::Sum=0;是定义静态数据成员;
3、静态数据成员和普通数据成员一样遵从public,protected,private访问规则;
4、因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;
static修饰类成员变量
class A
{
public:
int _a=1;
static int _b;
}
int A::_b=0;//在类外进行定义初始化
int main()
{
A a;
cout<<a._a<<endl;
cout<<a._b<<endl;
cout<<A::_b<<endl;//静态成员变量可以通过对象访问,也可以通过指定类域来访问。
system("pause");
return 0;
}
1.静态数据成员是该类的所有对象所共有的,对于该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用,因此对于所有的对象说静态成员变量的值都是相同的。
2.静态成员变量是在程序开始运行时被分配空间,到程序结束才释放,只要类中定义了静态成员变量,即使不定义对象也会为其分配空间。
3、类的静态成员变量必须先初始化再使用。
#include <stdio.h>
class Point
{
public:
Point()
{
m_nPointCount++;
}
~Point()
{
m_nPointCount--;
}
static void output()
{
printf("%d\n", m_nPointCount);
}
private:
static int m_nPointCount;
};
void main()
{
Point pt;
pt.output();
}
编译不错,链接成exe出错。静态资源是类初始化的时候加载的,而非静态资源是类实例化对象的时候加载的。 类的初始化早于类实例化对象,所以对于静态资源来说,它是不可能知道一个类中有哪些非静态资源的;但是对于非静态资源来说就不一样了,由于它是实例化对象出来之后产生的,因此属于类的这些东西它都能认识。
static修饰类成员函数
普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。通常情况下,this 是缺省的。如函数fn()实际上是this->fn()。
但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。 下面举个静态成员函数的例子。
class Myclass
{
public:
Myclass(int a,int b,int c);
static void GetSum();/声明静态成员函数
private:
int a,b,c;
static int Sum;//声明静态数据成员
};
int Myclass::Sum=0;//定义并初始化静态数据成员
Myclass::Myclass(int a,int b,int c)
{
this->a=a;
this->b=b;
this->c=c;
Sum+=a+b+c; //非静态成员函数可以访问静态数据成员
}
void Myclass::GetSum() //静态成员函数的实现
{ cout<<a<<endl; //错误代码,a是非静态数据成员
cout<<"Sum="<<Sum<<endl;
}
void main()
{
Myclass M(1,2,3);
M.GetSum();
Myclass N(4,5,6);
N.GetSum();
Myclass::GetSum();
}
1、出现在类体外的函数定义不能指定关键字static;
2、静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
3、非静态成员函数可以任意地访问静态成员函数和静态数据成员;
4、**静态成员函数不能访问非静态成员函数和非静态数据成员; **
5、由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;