zoukankan      html  css  js  c++  java
  • 存储持续性、作用域和链接性之二

    除了线程存储持续性,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)
    
  • 相关阅读:
    从大量的IP访问记录中找到访问次数最多的IP
    打赏功能的实现
    MFC通过ODBC连接Mysql程序
    VC++中使用MFC通过ADO连接数据库
    MySQL 5.1参考手册
    MFC ADO mysql 经验总结一
    VC连接MySQL
    VC连接MySQL
    MFC使用ADO对象开发数据库应用程序
    MFC中用Ado连接数据库
  • 原文地址:https://www.cnblogs.com/tianshihao/p/14128811.html
Copyright © 2011-2022 走看看