C++(内存分配)

内存分区

在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指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值