C++内存模型和动态内存分配

  C++使用三种(在C++11中是4种)不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间。
• 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储性为自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
• 静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性为静态 。它们在程序整个运行过程中都存在。C++有3种存储性为静态的变量。
• 线程存储持续性(C++11):当前,多核处理器很常见,如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。
• 动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被称为自由存储或堆。

5种变量存储方式

存储描述持续性作用域链接性如何声明
自动自动代码块在代码块中
寄存器自动代码块在代码块中,使用关键字register
静态,无链接性静态代码块在代码块中,使用关键字static
静态,外部链接性静态文件外部不在任何函数内
静态,内部链接性静态文件外部不在任何函数内,使用关键字static

存储说明符
• auto:在c++11之前使用auto指出变量为自动变量,但在C++11中用于自动类型推断。
• register:用于在声明中指示寄存器存储,在c++11中,它只是显示的指出变量是自动的。
• static:用在作用域为整个文件的声明中时,表示内部链接性;用在局部声明中时,表示局部变量的存储持续性为静态的。
• extern:表明是引用声明,即声明引用在其它地方定义的变量。
• thread_local:指出变量的持续性与其所属线程的持续性相同。
• mutable:如果有结构或类变量为const,但是它们有成员使用了mutable,则该成员也是可以被修改的。

cv限定符
• const;
• volatile

  通常编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,另一块用于动态存储。
使用new运算符初始化

int *pi = new int (6);
double *pd = new double (99.9);
int * ar = new int [4] {2, 3, 4, 5};

常规new运算符
运算符new和new[]分别调用如下函数:
void * operator new(std::size_t);
void * operator new[] (std::size_t);

int * pi = new int;将被转换如下
int * pi = new(sizeof(int));

int * pi = new int[40];将被转换如下
int * pi = new(40 * sizeof(int));

delete和delete[]与new和new[]类似,它们都是通过运算符重载实现的。
void operator delete(void *);
void operator delete[](void *);
  C++将这些函数称为可替换的。这意味着如果您有足够的知识和意愿,可为new和delete提供函数,并根据需要对其进行定制。例如可定义作用域为类的替换函数,并对其进行定制,以满足该类的内存分配需求。

定位new运算符
通常,new负责在堆中找到一个足以能够满足要求的内存块。new运算符还有另一种变体,被称为定位new运算符,它让您能够指定要使用的位置。

char buffer[500];
struct chaff
{
	int num;
}
class JustTesting
{
	string words;
	int number;
public:
	JustTesting(const string & s = "Just Testing", int n = 0)
	{words = s; number = n; cout << words << "constructed\n";}
	~JustTesting() {cout << words << "," << number << endl;}
};
int main()
{
	chaff * p0, * p1, *p2;
	JustTest * p3, p4;
	char * buf = new char[512];
	p0 = new (buffer) chaff;
	p1 = new (buffer) chaff;
	p2 = new (buffer + sizeof(chaff)) chaff;
	p3 = new JustTesting("Heap1", 20);
	p4 = new (buf) JustTesting;

	delete p3;
	delete []buf;
	p4->~JustTesting(); 
	delete p4;
}

对以上代码说明几点:
 1.buffer指定的内存是静态内存,而delete只能用于这样的指针:指向常规new运算符分配的堆内存。也就是说delete可与常规new运算符配合使用,但不能与定位运算符配合使用;
 2.buf是使用new[]创建的,必须使用delete[]运算符而不是delete;
 3.delete p4也是释放的buf,而不是p4,所以它并不会调用析构函数,也就是对象没有被销毁,正确的做法是显示的为使用定位new运算符创建的对象调用析构函数;
 4.p1的内容会覆盖p0,正确的方式是p2;因为使用定位new的时候,指针p0,p1都是等于buffer的地址的。

  定位new运算符的另一种用法是 ,将其与初始化结合使用,从而将信息放在特定的硬件地址处。

静态类成员
  静态类成员有一个特点:无论创建了多少个对象,程序都只创建一个静态类变量副本。不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存。需要在类外使用单独的语句来进行初始化,这是因为静态类成员是单独存储的,而不是对象的组成部分。它在包含类方法的文件中初始化。初始化时使用作用域运算符来指出静态成员所属的类。但如果静态成员是整形或枚举型const,则可以在类声明中初始化。

注意:在构造函数中使用new来分配内存时,必须在相应的析构函数中使用delete来释放内存。如果使用new[]来分配内存,则应使用delete[]来释放内存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值