【C++Primer Plus Chapter9】 内存模型和名称空间(2)
1. 存储说明符和cv-限定符
- auto
- register
- static
- extern
- mutable
- const
- volatile
2. auto 自动变量。
3. register 寄存器变量。
4. static 静态变量,用于作用域为整个文件的声明时,表示内部连接性;用于局部声明时,便是局部变量的存储持续性为静态的。
5. extern 表明是引用声明,引用其他地方定义的变量。
6. mutable 指出即使结构体(或类)变量为 const,被关键词 mutable 修饰的成员也可以被修改。
struct data{ char name[30]; mutable int accesses; }; const data veep = {"Claybourne Clodde", 0}; strcpy(veep.name, "Joye Joux"); //invalid veep.acesses++; //valid
7. const 内存被初始化后,程序便不能对其进行修改。
//NOTE1:const 修饰的全局变量的链接性为内部的。(即,在C++看来,全局const定义就像使用了static存储说明符)
//file1 #include <iostream> using namespace std; const int a = 10; //内部链接性 void show(); int main(){ cout << a << " " << &a << endl; show(); return 0; } //file2 #include <iostream> using namespace std; const int a = 20; //内部链接性 void show(){ cout << a << " " << &a << endl; } /*OUTPUT 10 0x4b902c 20 0x4b9024 */
//NOTE2:若希望某个常量的链接性为外部的,则可以使用关键字 extern 覆盖默认的内部链接性,这种情况下所有使用该常量的文件都要使用 extern 来引用声明它,只能在其中一个文件初始化该变量,且之后该变量不可修改。
//file1 #include <iostream> using namespace std; extern const int a = 10; //外部链接性 void show(); int main(){ cout << a << " " << &a << endl; show(); return 0; } //file2 #include <iostream> using namespace std; extern const int a; //外部链接性 //extern const int a = 20; invalid initialize void show(){ cout << a << " " << &a << endl; } /*OUTPUT 10 0x4b9028 10 0x4b9028 */
8. volatile 即使程序代码没有对内存单元进行修改,其值也可能发生变化。该关键字的作用是为了改善编译器的优化能力。
A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided.
//NOTE1:volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统,硬件或者其他线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。
//NOTE2:一个参数可以同时被关键字 const 和 volatile 修饰。 const修饰只是表明不能被程序修改,但是可以被硬件修改,因此也可以在需要时用 volatile 修饰。
//NOTE3:
下面是volatile变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量
9. 函数的链接性
所有函数的存储持续性都自动为静态的。默认情况下函数的链接性为外部的。可以显式使用extern关键字修饰函数表明该函数是在另一个文件中定义的。
可以使用关键字 static 将函数的链接性改为内部的。函数原型和定义中都需要static修饰。
10. 语言链接性
由于C++函数可以重载,C++编译器会执行名称矫正或名称修饰。连接程序寻找与C++函数调用匹配的函数时使用的方法与C语言不同。可以在函数原型中指出要是用的约定:
extern "C" void func(int); extern "C++" void func2(int); extern void func3(int); //默认使用C++约定查找名称
名称空间
简化大型编程项目的管理工作。
C++中的名称:变量、函数、结构、枚举、类以及类和结构的成员。
- 声明区域:可以在其中进行声明的区域
- 潜在作用域:从声明点开始
- 作用域:变量对程序而言可见的范围
1)名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。
2)默认情况下,名称空间中声明的名称的链接性为外部的。
3)全局名称空间对应于文件级声明区域,全局变量位于全局名称空间中。
访问空间中的名称:
1. 使用作用域解析操作符::
2. 使用using声明
//NOTE1:using声明使一个名称可用,using编译指令使所用名称可用。
3. 使用using namespace编译指令
//NOTE1:在全局声明区域使用,将使该名称空间中的名称全局可用。
//NOTE2:在函数中使用,将使该名称空间中的名称在该函数中可用。
#include <iostream> using namespace std; namespace ns1{ int apple; } namespace ns2{ int pear; using ns1::apple; namespace ns3{ int orange; } } int main(){ using ns1::apple; cout << apple << " " << &apple << endl; //0 0x4c600c using namespace ns2; cout << ns2::apple << " " << &(ns2::apple) << endl; //0 0x4c600c 嵌套的名称空间可以直接使用最外层的名称空间限定 }
//NOTE3:假设名称空间和声明区域定义了相同的名称。则不能使用using声明将名称空间中的名称导入该声明区域。并且使用using编译指令导入名称空间中的名称时,局部版本将会隐藏名称空间版本。
#include <iostream> using namespace std; namespace ns1{ int apple; } namespace ns2{ int pear; using ns1::apple; namespace ns3{ int orange; } } int main(){ int apple = 1; cout << apple << " " << &apple << endl; //1 0x6dfeec //using ns1::apple; invalid using namespace ns2; cout << ns2::apple << " " << &(ns2::apple) << endl; //0 0x4c600c }
//NOTE4:不能在未命名名称空间所属文件之外的其它文件中使用该名称空间中的名称。该方法可以代替链接性为内部的静态变量。
//NOTE5:名称空间中声明的函数名的作用域为整个名称空间,函数定义和声明必须位于同一个名称空间。
//file 1 namespace ns{ struct Person{ char fname[100]; char lname[100]; } void get(Preson &); void show(const Preson &) } //file 2 namepace ns{ using std::cout; using std::cin; viod get(Person &){ ... ... } void show(const Person &){ ... ... } }
//NOTE6:重载函数,using声明将导入所有版本。