内存模型
- 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
- 静态存储持续性:在函数定义外定义的变量和使用关键字
static
定义的变量的存储持续性都为静态。它们在程序整个运行过程中都存在。C++有3种存储持续性为静态的变量。 - 线程存储持续性(C++11):当前,多核处理器很常见,这些CPU可同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程中。如果变量是使用关键字
thread_local
声明的,则其生命周期与所属的线程一样长。 - 动态存储持续性:用
new
运算符分配的内存将一直存在,直到使用delete
运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被称为自由存储(free store)
或堆(heap)
。
c++ 变量的默认类型为自动存储持续性的 C语言中
auto
关键字可以指出当前的变量为局部自动变量,但是c++中auto
有了别的作用,即自动类型推导,使用auto
来指定变量为自动存储的手段将不再合法
C语言中还支持寄存器变量使用
register
关键字来让寄存器存储自动变量,在C++11之后这种用法失效了,而是变成了用于指定一个变量为自动变量,这与C语言以前的auto
完全相同
静态存储持续性与static
关键字
写在函数体外的变量就是静态持续性的
int x =1000; //静态持续性,具有外部链接
static int y =50; // 静态持续性,内部链接
int main(){}
void func(){
static int c=0; // 静态持续性 ,链接
}
在C++中,链接属性(linkage)决定了标识符(变量、函数、类等)在不同翻译单元(即源文件)中的可见性。
- 外部链接: 可以在程序的任何翻译单元中访问,在整个程序中必须是唯一的定义
- 内部链接:仅在该翻译单元可见,不同翻译单元可以有同名的内部链接实体
- 无连接:仅在定义它的作用域内可见
C++中
const
全局变量默认内部链接
C++有“单定义规则”(One Definition Rule,ODR),该规则指出,变量只能有一次定义。
多文件程序是不允许有相同名字的具有外部链接的变量的
// file1
int x =10;
...
--------------------------
//file 2
int x =20;
int main(){}
g++ file1 file2
会报错,是不能有相同名称外部变量的
此时你需要使用static
关键字将一个变量设置为内部链接,或者使用如下的extern
变量让一个定义变成引用声明而不是定义声明
所以C++除了传统的变量定义声明,通过extern
关键字实现了引用声明,用来指示这个变量来自其他的源文件,且不能进行初始化,如果初始化了就代表他是定义声明 , 引用声明不会分配存储空间
extern int x; // 引用声明
extern int y=1; // 定义声明
函数默认是静态持续性的,也可以使用static
让其具备内部链接性(请注意函数的定义也要满足单定义规则),使用外部的函数可以使用extern
关键字声明
static
无法修饰模板,类、结构 ,你可以使用匿名名称空间来让这些内容具有内部链接性
说明符和限定符
存储说明符
auto
C++11已经不再是说明符register
static
extern
thread_local
C++11新增的线程使用mutable
限定符
-
volatile
表面即使程序没有对单元进行修改他的值也可能发生变化,例如硬件改变,用于嵌入式编程,避免编译器对这个值进行优化 -
mutable
可以用它指出即使结构或者是类变量为const
,其中的某个成员也可以被修改struct data{ char name[30]; mutable int accesses; }; const data v{"bueryi",0}; v.accesses++; // 虽然结构为const,但是你仍然可以修改被mutable修改的变量
-
const
C++ 与C语言不同,const
限定符修饰的全局变量链接性为内部的(C语言则是外部的),也就是说C++中全局const
定义就像是使用static
一样const int x = 10; // 和 static const int x =10; 相同
这样在你将常量放入头文件的时候,可以更加轻松地导入文件,如果他和其他变量同样是外部的话,在你不同源文件都包含相同的头文件时,会由于有多个具有外部链接的变量而出错,违反单变量规则
也就是这样只有一个文件可以包含前面的声明,其他文件必须使用extern
关键字extern const int x;
所以C++中
const
全局变量被设置为内部链接的形式,每一组文件都会有自己的一组常量,而不是所有的文件共享一组常量,每个定义都是所属文件私有的如何声明外部链接的常量呢?使用
extern
关键字extern const int x =50;
这个行为与普通变量不同,普通变量默认就是外部链接,无需使用
extern
指定下面我举例说明一下测试
//head.h #pragma once const int x=10; //这是内部链接的常量 //file0.cpp extern const int y=123; //直接声明了一个外部链接的常量 //file1.cpp #include <iostream> #include "head.h" void f1() { using std::cout; extern const int y; cout << "file1: \n"; cout << "x= "<< x << " &x="<< &x<<std::endl; cout << "y= "<< y << " &y="<< &y<<std::endl; } //file2.cpp #include <iostream> #include "head.h" void f2() { using std::cout; extern const int y; cout << "file2: \n"; cout << "x= "<< x << " &x="<< &x<<std::endl; cout << "y= "<< y << " &y="<< &y<<std::endl; } //main.cpp #include <iostream> extern void f1(); //调用外部的函数 extern void f2(); int main() { f1(); f2(); return 0; }
所有的文件如下所示(请忽略我的
cpplearn
)
编译运行(我没有指定输出名字,默认是a.out
, 我还使用了通配符,你可以在这里找到通配符的使用)
你可以看到file1
和file2
他们的x
地址不同,这体现了前面所说的,const
默认是内部链接,不同的文件导入会产生各自的常量,他们的地址不同,而y
是外部const
变量,你可以看到不同文件引用y
他们的地址是相同的(这与常规引用外部变量的行为一直,extern
是声明定义不会初始化储存空间)
动态变量
C++使用new
关键字来从堆中获取内存空间,他的生命周期持续到你调用delete
为止
支持调用时初始化
int* arr2 = new int[5]{1, 2, 3, 4, 5};
定位new
char buffer[300]; // 预分配内存
double* pt = new (buffer) double[10]; // 在指定内存构造对象
注意此时不能使用delete []
来释放空间,因为他不在堆中,而是你自己预分配的地方(除非预分配的内存也是用new
分配的)
new/delete
与malloc/free
的区别
new
会调用构造函数和析构函数,malloc
不会new
会返回正确的指针,malloc
则是void*
指针你需要强制类型转换new
失败了会抛出bad_alloc
异常,而malloc
则会返回NULL
new
会自动计算分配内存大小,malloc
则是要手动计算new
支持重载,malloc
不行