学习C++.Primer.Plus 9 内存模型和名称空间
1.有关头文件
- 头文件中不要添加变量和函数定义。(多次引用会重复,内联函数除外)
- 可包含在头文件中的内容有:函数/结构/类/模板的声明,使用#define或const()定义的符号常量。
- 可包含在头文件中的内容有:函数/结构/类/模板的声明,使用#define或const()定义的符号常量。
- #include<>只查找系统库,#include””先查找工作目录,没有再找系统库
- C++防止多重声明的防护方案:
这样一来,即使包含了多次头文件,也只会忽略除了第一次之外的所有内容//testProtect.h #ifndef TESTPROTECT_H #define TESTPROTECT_H //头文件部分... //... //... #endif
2.存储持续性、作用域、链接性
函数的作用域不能是局部的,因为函数不能在代码块内部定义。但链接性可以设置为内部的,具体见#12
- 静态、自动、动态变量的涵义都是相对于存储的持续性来讲的。
静态变量不会在程序运行期间自动销毁。
如下所示:
int x = 0;//静态变量,链接性为外部 static int y = 0;//静态变量,链接性为内部 int main() { ... } void functin1() { static int z = 290;//静态变量,无链接性,只在本函数中可用,程序开始就存在。 }
- 局部代码块中的变量会暂时隐藏全局代码块中的同名变量。
此时要引用外部变量时可以在前面添加作用域解析操作符(::)。 ::sheepCount。
- 用auto声明一个自动持续性变量,默认声明时就是。
auto float a = 5.6;
- C++通过使用堆栈来实现自动变量:程序预先留出一块内存视为堆栈来存储自动变量,开始一个指针指向栈底,一个指针指向栈顶,新增自动变量时加入栈顶,指针后移。自动变量结束时,栈顶重新指向开始时的位置。
- 寄存器变量:也是一种自动局部没有链接性的变量,但是存储在CPU寄存器中,访问速度比较快。
register int int_fast; //注:该变量没有内存地址。只有当被频繁使用时声明
- 多文件程序中,只能在一个文件中定义一个外部变量,即使没有外部引用,也不能多次定义外部变量。
- mutable存储说明符: 即使结构或类变量是const类型,其某个成员也可以被修改:
struct data { char name[30]]; mutable int accesses;//即使声明了const类型的该值也可以被修改。。。 }; const data veep = {"kkks", 0}; veep.accesses = 19;
- volatile 关键字:(cv限定符的一种。const, volatile)。不让程序将变量放入寄存器。(默认情况下编译器会将多次引用的值放入寄存器,下次直接使用而不需要去查找该值)
例如,把某个指针指向硬件位置,下次使用时再去查找该地址,有可能值已经被其它程序改变了。
- extern 关键字:引用声明。
如果在文件的外部代码块中声明了全局(作用域)外部(链接性)变量 int sheepCount = 3;
要在另外一个文件B中使用它,需要在B中添加 引用声明 :
extern double sheepCount;//注意,这里不能进行初始化,只有为变量分配内存空间时才能进行初始化。
- const 关键字:const关键字会使默认存储类型的链接性变成内部的(就像static一样)。这就是为什么可以放在头文件中了。如果多个文件都引用该头文件,每个文件中的const类型的常量都是内部链接性的,所以就不会出错。
const int conValue = 88;//链接性为内部的。
此时,如果要使const常量的链接性变为外部,可以通过extern关键字来来覆盖默认的内部链接性。与常规的 extern变量不同的是,此时的extern const变量必须被初始化,另外一个不同点是:在所有使用它的文件中都必须使用extern(包括定义的文件中)。
extern const int states = 50; //链接性为外部的常量
- static 关键字:声明静态变量。会使外部链接性的变量的链接性变成内部的。在程序运行期间一直存在。
即使static变量在代码块内部,也会将变量的存储持续性声明为静态的。在程序启动时初始化一次,以后再调用时不会重新初始化,值保留上次的值。
static int internal_value; //会使外部链接性的变量的链接性变成内部的
static关键字还可以将函数的链接性设置为内部的,只能在一个文件中使用。
- 单定义规则:多文件程序中,只能有一个文件包含某函数的定义,并且使用非内联函数的每个文件都要包含其函数原型。
但内联函数不受该规则约束,可以在多个文件中包含定义,不过C++要求同一个函数的所有内联定义都必须相同。
- 语言链接性:不同编译器对同一函数生成的修饰名称不同,因此在链接编译模块时,要确保所有文件都是有同一编译器生成的。
比如下面使用函数原型来指出要使用的约定:
extern "C" void spiff (int); //将使用C语言的名称修饰格式来查找要函数 extern void spoff (int); extern "C++" void spaff (int); //以上两个均采用C++格式的语言修饰来查找函数
- 一些小概念:
编译器使用3块独立的内存:一块用于存放静态变量, 一块用于自动变量, 另一块用于动态存储。
动态内存不受作用域和链接性规则控制,不适用于存储方案。但用来跟踪动态内存的指针适用于存储方案。
如果要让一个跟踪动态内存的指针可以在多个文件中使用,可以把它定义成静态外部链接性的:float * p_fees;
extern float* p_fees;
float * p_fees; //float * p_fees = new float[20];//不可取,非常量,不能用来初始化静态变量。 void main() { p_fees = new float[20];//正确 }
3.布局new操作符
- 布局 new 操作符与普通 new 操作符不同,它可以在指定位置分配空间。
使用布局 new 操作符需要包含 new 头文件。#include <new> struct student { ... } int main() { char buf1[50]; char * p2 = new (buf1) student;//在buf1中分配一个结构 char * p3 = new (buf1) int[20];//在buf1中分配一个数组 }
- 使用 布局 new 操作符分配的空间不能使用 delete 或 delete[] 来删除。
4.名称空间
- 几个概念:
声明区域:变量声明所在的区域;如函数外的全局静态变量,声明区域为整个文件。
潜在作用域:从声明开始到声明区域结束的位置。
作用域:变量可见的范围。
- 根据是否带名称空间装饰,变量又分为 未限定的名称(pail) 和 限定的名称(Jack::pail)。
- using 声明:using Jack::pail; 这将变量添加到了局部声明区域中,因此不能再声明另一个同名的变量。
using 编译指令:using namespace Jack; 使名称空间中的所有都可用,此时可以声明与名称空间中名称同名的局部变量,这样局部版本变量会暂时隐藏名称空间版本的全局变量。
在选择使用 using声明 和 using编译指令 时,应尽量选择使用 using 声明。
且在使用using声明时,首选将其作用域设置为局部的。
- 嵌套的名称空间的特性:
namespace Jill { int pail; namespace element { namespace fire { int countSheep; ... } ... } ... } //另一名称空间中 namespace myth { using namespace Jill::pail;//此时使用using声明引入pail局部变量 cin >> Jill::pail; cin >> myth:pail;//可以同时用这两种方法来调用pail,因为它同时在这两个名称空间中 }
同时还可以给名称空间创建别名:namespace AliasN = myth::elements::fire; using fire::countSheep;
- C++不赞成在名称空间 或 全局作用域中使用关键字 static ,替代的方法是使用 未名称的名称空间:未命名的名称空间中的变量只有在当前文件中是可见的。
namespace myth { static int counts;//静态变量,链接性为内部。。不赞成使用的(不管是否在名称空间中) namespace { int counts2;//静态变量,链接性为内部。。。赞成这样使用 ... } }
- 附:老式的头文件(如iostream.h)没有使用名称空间,但新式的头文件(iostream)使用的std名称空间。
注: