存储持续性
- 自动存储持续性:在函数定义中声明的变量,作用域为局部,没有链接性
- 静态存储持续性:在函数外部定义的变量和使用static定义的变量
- 线程存储持续性:使用thread_local声明的变量
- 动态存储持续性:用new分配的内存
静态持续变量
有三种链接性:
- 外部链接性(可在其他文件中访问):不在任何函数内声明
- 内部链接性(只能在当前文件中访问):不在任何函数内,用static声明
- 无链接性(只能在当前函数或代码块中访问):在代码块中,用static声明
静态持续变量默认为0。
变量只能有一次定义(单定义规则),但在每个使用外部变量的文件中都必须声明它。因此有两种变量声明:定义声明和引用声明。
引用声明使用extern,且不进行初始化;否则,声明为定义,会分配内存空间。
单定义规则并非意味着不能有多个变量的名称相同,例如,在不同函数中声明的同名变量是彼此独立的,他们都有自己的地址。
如果在函数中生命了一个与外部变量同名的变量,将被视为一个自动变量的定义。
程序9.5
#include <iostream>
using namespace std;
//external variable
double warming = 0.3;
void update(double dt);
void local();
int main()
{
cout << "Global warming is " << warming << " degrees.\n";
update(0.1);
cout << "Global warming is " << warming << " degrees.\n";
local();
cout << "Global warming is " << warming << " degrees.\n";
system("pause");
return 0;
}
程序9.6
#include <iostream>
extern double warming;
void update(double dt);
void local();
using std::cout;
void update(double dt)
{
extern double warming;
warming += dt;
cout << "Updating global warming to " << warming;
cout << " degrees.\n";
}
void local()
{
double warming = 0.8;
cout << "Local warming " << warming << " degrees.\n";
cout << "But global warming = " << ::warming;
cout << " degrees.";
}
程序9.5和9.6的出输出表明,main()和update()都可访问warming,update()修改了warming。
local()函数表明定义与全局变量同名的局部变量后,局部变量将隐藏全局变量。
C++提供了作用域解析符(::),放在变量名前,表示使用变量的全局版本。
如果要在其他文件中使用相同的名称来表示其他变量,只省略extern是不够的,还需要将一个文件中的变量定义为静态外部变量(static)。
在多文件程序中,可以在一个文件(且只能在一个文件)中定义一个外部变量,使用该变量的其他文件必须使用关键字extern声明。
无链接性的局部变量:将static限定符用于代码块中定义的变量。这将导致局部变量的存储持续性为静态的,虽然该变量只在代码块中可用,但他在该代码块不处于活动状态时仍然存在。因此在两次函数调用之间,静态局部变量的值将保持不变。
程序9.9
#include <iostream>
const int ArSize = 10;
void strcount(const char * str);
int main()
{
using namespace std;
char input[ArSize];
char next;
cout << "Enter a line:\n";
cin.get(input, ArSize);
while (cin)
{
cin.get(next);
while (next != '\n')
cin.get(next);
strcount(input);
cout << "Enter next line (empty line to quit):\n";
cin.get(input, ArSize);
}
cout << "Bye\n";
system("pause");
return 0;
}
void strcount(const char * str)
{
using namespace std;
static int total = 0; //static local variable
int count = 0;
cout << "\"" << str << "\" contains ";
while (*str++)
count++;
total += count;
cout << count << " characters\n";
cout << total << " characters total\n";
}
该程序演示了一种处理行输入可能长于目标数组的方法。方法cin.get(input,ArSize)将一直读取输入,直到到达行尾或读取了ArSize-1个字符为止。它把换行符留在输入队列中。该程序使用cin.get(next)读取行输入后的字符。如果next是换行符,表明cin.get(input,ArSize)读取了整行;否则说明还有字符没有被读取。随后利用一个循环来丢弃余下的字符。另外,该程序说明用get(char *,int)读取空行将导致cin为false。
每次函数被调用时,自动变量count都被重置为0;然而静态变量total只在程序运行时被设置为0,之后每次调用,其值都保持不变。
说明符和限定符
存储说明符:
- auto(在C++11中不再是说明符,而用于自动类型推断)
- register(在声明中指示寄存器存储)
- static
- extern
- thread_local(C++11新增的)
- mutable
cv-限定符:
- const
- volatile
volatile作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化。
mutable
mutable用来指出,即使结构或类变量为const,其某个成员也可以被修改。
struct data
{
char name[30];
mutable int accesses;
...
};
const data veep = {"Claybourne Clodde", 0, ...};
strcpy(veep.name,"Joye Joux"}; //不允许
veep.accesses++; //允许
在C++中,const对默认存储类型有影响:在默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部的。就像使用了static一样。
如果出于某种原因,希望某个常量的链接性为外部的,则可以使用extern来覆盖默认的内部链接性:
extern const int states = 50;
函数的链接性
函数也有链接性,默认的链接性是外部的,即可以在文件件共享。实际上,可以在函数原型中使用extern来指出函数实在另一个文件中定义的,不过这是可选的。
还可以使用static将函数的链接性设置为内部的,使之只能在一个文件中使用,必须同时在原型和定义中使用static。
不允许在一个函数中定义另一个函数,因此所有函数的存储持续性都自动为静态的,即在整个程序执行期间都一直存在。
动态分配
使用new来分配动态内存,使用delete来释放内存,而不由作用域和链接性规则控制。因此,可以在一个函数中分配动态内存,而在另一个函数中将其释放。
使用new初始化
C++98:
int *pi = new int (6); //*pi为6
double *pd = new double (99.99); // *pd为99.99
C++11:
列表初始化
struct where {double x; double y;double z;};
where * one =new where {2.5, 5.3, 7.2};
int * ar = new int [4] {2, 4, 6, 7};
int *pin = new int {6};
double *pdo = new double {99.99};
“定位new”(placement new)运算符
通常new在堆(heap)中寻找一个足以满足要求的内存块。
定位new运算符可以指定要使用的位置,使用定位new,需要包含头文件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
p2 = new (buffer1) chaff; //将结构放置于buffer1
p4 = new (buffer2) int [20]; //将int 数组放置于buffer2
...
}
程序9.10
#include <iostream>
#include <new>
const int BUF = 512;
const int N = 5;
char buffer[BUF];
int main()
{
using namespace std;
double *pd1, *pd2;
int i;
cout << "Calling new and placement new:\n";
pd1 = new double[N];
pd2 = new (buffer) double[N];
for (i = 0; i < N; i++)
pd2[i] = pd1[i] = 1000 + 20.0*i;
cout << "Memory addresses:\n" << " heap: " << pd1
<< " static: " << (void *)buffer << endl;
cout << "Memory contents:\n";
for (i = 0; i < N; i++)
{
cout << pd1[i] << " at " << &pd1[i] << ";";
cout << pd2[i] << " at " << &pd2[i] << endl;
}
cout << "\nCalling new and placement new a second time:\n";
double *pd3, *pd4;
pd3 = new double[N]; //find new address
pd4 = new (buffer) double[N]; //overwrite old data
for (i = 0; i < N; i++)
pd4[i] = pd3[i] = 1000 + 40.0*i;
cout << "Memory contents:\n";
for (i = 0; i < N; i++)
{
cout << pd3[i] << " at " << &pd3[i] << ";";
cout << pd4[i] << " at " << &pd4[i] << endl;
}
cout << "\nCalling new and placement new a third time:\n";
delete[] pd1;
pd1 = new double[N];
pd2 = new (buffer + N * sizeof(double)) double[N];
for (i = 0; i < N; i++)
pd2[i] = pd1[i] = 1000 + 60.0*i;
cout << "Memory contents:\n";
for (i = 0; i < N; i++)
{
cout << pd1[i] << " at " << &pd1[i] << ";";
cout << pd2[i] << " at " << &pd2[i] << endl;
}
delete[] pd1;
delete[] pd3;
system("pause");
return 0;
}
该程序没有使用delete来释放定位new运算符分配的内存,事实上,这个例子不能这样做。因为buffer指定的内存是静态内存,而delete只能用于指向常规new运算符分配的堆内存。也就是说,buffer位于delete的管辖范围之外。
如果buffer是使用常规new创建的,则可以使用delete。