C++内存模型

来源:互联网 发布:java语言程序设计txt 编辑:程序博客网 时间:2024/06/05 11:21

程序开发时常使用多个文件。一般组织策略是,使用头文件定义数据类型,以及提供操纵这些数据类型的函数原型;将函数定义放在一个独立的源代码文件中;将主函数和使用操纵这些数据类型的函数放在其它的文件中。然而,将一个程序放在多个文件中会引出一些新的问题,即信息在多文件中是怎样共享的呢?这涉及到三个概念存储持续性、作用域和链接性。

       存储持续性描述了名称在文件中持续的时间。

       作用域描述了名称在文件中的可见范围。

       链接性描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,链接性为内部的名称只能在所属的文件中共享。自动变量没有链接性,因为它们不能共享。

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

C++使用三种方案(在C++11中是四种)不同的方案来存储数据,这些方案的区别在于数据保留在内存中的时间。四种存储类型如下:

1、自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。

2、静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。它们在程序的整个运行过程中都存在。C++有三种存储持续性为静态的变量。

3、线程存储持续性(C++11):在多核处理器中,CPU可同时处理多个执行任务。因此,可将计算放在可并行处理的不同线程中。如果变量是使用关键字threa_local声明的,则其生命周期与所属的线程一样长。

4、动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态。


1.1自动存储持续性变量


在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。可以使用任何在声明时其值为已知的表达式来初始化自动变量。自动变量存储在栈中。自动变量的存储方式见下表:

表:自动变量的存储方式

存储描述

持续性

作用域

链接性

如何声明

 

自动

 

 

自动

 

代码块

 

在代码块中

或使用关键字auto,C++11之后使用register

寄存器

自动

代码块

在代码块中,使用关键字register

测试如下:
#include <iostream>using std::cout;using std::endl;void autoVariableTest(int x);int main(){    int test = 7;    cout << "test = " << test << " The address of main-test is "         << &test << endl;    autoVariableTest(test);    cout << "test = " << test << " The address of main-test is "         << &test << endl;    return 0;}void autoVariableTest(int x){    int test = x;    cout << "test = " << test << " The address of auto-f-test is "         << &test << endl;    cout << "x = " << x << " The address of x is " << &x << endl;    {        int test = 6;        cout << "test = " << test << "The address of auto-f-block-test is "             << &test <<endl;    }    test = 8;    cout << "test = " << test << " The address of auto-f-test is "         << &test << endl;}



1.2静态存储持续性变量

和C语言一样,C++为静态存储持续性变量提供了3种链接性:外部链接性,可在其它文件中访问;内部链接性,只能在当前文件中访问;无链接性,只能在当前函数或代码块中访问。这3中链接性都在整个程序执行期间存在,与自动变量相比,它们的持续时间更长。编译器一般分配固定的静态内存来存储所有的静态变量,如果没有显示初始化静态变量,编译器将它设置为0,但自己进行变量初始化是一个良好的编程习惯。静态变量的存储方式见下表:

表:静态变量存储方式

存储描述

持续性

作用域

链接性

如何声明

静态,无链接性

静态

代码块

在代码块中,使用关键字static

静态,内部链接性

静态

文件

内部

不在任何函数内,代码块中,使用关键字static

静态,外部链接性

静态

文件

外部

不在任何函数内和代码块中

 


测试如下:

file1.cpp#include <iostream>using std::cout;using std::endl;double add(double x1, double x2);double test_file1 = 0; //静态变量,外部链接性double test_file2 = 0; //测试文件2中的内部链接性变量test_file2是否会改变此值extern double test_file3; //测试与文件2中的外部链接性变量test_file3是否冲突,发现是有冲突的,需加externint main(void){    int x = 1;    int y = 2;    cout << "x = " << x << " y = " << y << endl;    cout << "test_file1 = " << test_file1 << endl;    cout << "test_file2 = " << test_file2 << endl;    cout << "test_file3 = " << test_file3 << endl;    test_file3 = add(x, y);    test_file3 += add(x, y);    test_file3 += add(x, y);    cout << "test_file3 = " << test_file3 << endl;    cout << "test_file1 = " << test_file1 << endl;    cout << "test_file2 = " << test_file2 << endl;    cout << "test_file3 = " << test_file3 << endl;    return 0;}
file2.cpp#include <iostream>extern double test_file1; //test_file1来自于其他文件static double test_file2; //静态变量,内部链接性double test_file3 = 0; //静态变量,外部链接性double add(double x1, double x2){    static int count = 0; //静态变量,无链接性    double y = x1 + x2;    test_file1 = y;    test_file2 = y;    test_file3 += y;    count++;    std::cout << "-------add------" << std::endl;    std::cout << "test_file1 = " << test_file1 << std::endl;    std::cout << "test_file2 = " << test_file2 << std::endl;    std::cout << "test_file3 = " << test_file3 << std::endl;    std::cout << "count = " << count << std::endl;    std::cout << "-------add------" << std::endl;    return y;}

需要注意一下几点:

1、         在每个使用外部变量的文件中,都必须声明它;此外,需遵循单定义规则,即变量只能定义一次。

2、         如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(单定义规则),但在使用该变量的其他文件中,都必须使用关键字extern声明它。

3、         C++提供了作用域解析符::,放在变量名称前面表示使用该变量的全局版本。

4、         初始化了静态局部变量,则程序只在启动时进行一次初始化。以后再调用函数时,将不会像自动变量那样再次被初始化。

5、         const全局变量默认链接性为内部的。如果需要将const全局变量设置为外部链接性,则在每个文件中都必须在定义或声明之前加上extern,如 extern constint flag = 2;


1.3       语言链接性

链接程序要求每个不同的函数都有不同的符号名。在C语言中,一个名称只对应一个函数,如doubleadd(double, double)翻译为_add, 这种方法称为C语言链接性。而在C++中,由于重载等语言特性,一个名称可能对应多个函数,因此不需将这些函数翻译为不同的符号名称,所以C++编译器执行名称矫正或名称修饰,为重载函数生成不同的符号名称,如doubleadd(double, double)翻译为_add_d_d, double add(int,int)翻译为_add_i,这种方法称为C++语言链接性。可以使用C++标准制定的链接性说明符解决这一问题。

如:

 extern “C” double add(int ,int); //按C原型查找

 extern “C++” double add(int ,int); //按C++原型查找

 extern double add(int ,int); //按C++原型查找

 

1.4         动态分配

1、在分配动态内存时,可以使用new运算符进行初始化,如:

int* pi = newint(6);

int* pi = newint[4] {2, 3, 4, 5}; //C++11

2、运算符new,new[], delete,delete[]调用一下形式的函数,并可被替换与重载。亦即自己可以对其进行定制。注意:delete只能用于释放指向常规new运算符分配的内存。

void* operator new(std::size_t);

void* operator new[](std::size_t);

void* operator delete(std::size_t);

void* operator delete[](std::size_t);

3、定位new运算符

定位new运算符可以在指定的位置上分配内存,使用定位new运算符需要包含头文件new,注意不能使用delete来释放定位new运算符分配的内存。定位new运算符可被重载,但不能被替换。

如: void* operatornew(std::size_t, void*);






0 0
原创粉丝点击