存储持续性、作用域和链接性之二

除了线程存储持续性,C++使用三种不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间。

  • 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性是自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
  • 静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性为静态。它们在程序整个运行过程中都存在。C++有三种存储持续性为静态的变量。
  • 动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的持续性为动态,有时也被成为自由存储(free store)和堆(heap)。

动态存储持续性

使用C++运算符new(或C函数malloc())分配的内存,被成为动态内存。动态内存由运算符newdelete控制,不是由作用域和链接性规则控制。因此,可以在一个函数中动态分配内存,而在另一个函数中将其释放。其分配和释放的顺序取决于newdelete在何时以何种方式被使用。

虽然存储方案概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态指针变量。例如,假设在一个函数中包含下面的语句:

float * p_fees = new float [20];

new分配的80个字节(假设float 为4个字节)的内存将一直保留在内存中,直到使用delete运算符将其释放。但当包含该声明的语句块执行完毕时,p_fees 指针将消失。如果希望另一个函数能够使用这80个字节中的内容,则必须将其地址传递或返回给该函数。另一方面,如果将p_fees 的链接性声明为外部的,则文件中位于该声明后面的所有函数都可以使用它。另外,通过在另一个文件中使用下述声明,便可在其中使用该指针:

extern float * p_fees;

使用new运算符初始化

如果要为内置的标量类型(如 intdouble)分配存储空间并初始化,可在类型名后面加上初始值,并将其用括号括起:

int *pi = new int (6); // *pi set to 6
double * pd = new double (99.99); // *pd set to 99.99

然而,要初始化常规结构或数组,需要使用大括号的列表初始化,这要求编译器支持C++11。C++11允许您这样做:

struct where { double x; double y; double z; };
where * one = new where {2.5,5.3,7.2}; // C++11
int * ar = new int [4] {2,4,6,7}; // C++11

在C++11中,还可将列表初始化用于单值变量:

int *pi = new int {6}; // *pi set to 6
double * pd = new double {99.99}; // *pd set to 99.99

new失败时

new可能找不到请求的内存量。在最初的10年中,C++在这种情况下让 new返回空指针,但现在将引发异常std:.bad_alloc。

定位new运算符

通常,new负责在堆(heap)中找到一个足以能够满足要求的内存块。new运算符还有另一种变体,被称为定位(placement) new运算符,它让您能够指定要使用的位置。程序员可能使用这种特性来设置其内存管理规程、处理需要通过特定地址进行访问的硬件或在特定位置创建对象。即定位new运算符可以在指定地址创建对象、将指定内存分配给指定对象。

#include <new>

struct chaff
{
    char dross[20];
    int slag;
};
char buffer1[50]
char buffer2[500];

int main()
{
    chaff *p1, *p2;
    int *p3, *p4;
    
    // 首先, new的常规形式
    p1 = new chaff;     // 放置结构体到堆中
    p3 = new int[20];   // 放置整型数组到堆中
    
    // 然后, 两种形式的定位new, placement new
    p2 = new (buffer1) chaff;   // 放置结构体放到 buffer1 中
    p4 = new (buffer2) int[20]; // 放置整型数组到 buffer2 中
    ...
}

注意,buffer1buffer2都位于delete的管辖区域之外,不能使用delete释放内存块。另一方面,如果buffer是使用常规new运算符创建的,便可以使用常规delete运算符来释放整个内存块。

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

定位new的其他形式

int * pi = new int;                 // invokes new(sizeof (int))
int * p2 = new(buffer) int;	        // invokes new(sizeof(int),buffer)
int * p3 = new(buffer) int [40];    // invokes new(40*sizeof(int), buffer)
原文地址:https://www.cnblogs.com/tianshihao/p/14128811.html