第九章 内存模型和名称空间
1、不要将函数定义或者变量声明放到头文件中。
2、头文件常包含的内容:函数原型、使用#define或者const定义的常量、结构声明、类声明、模板声明、内联函数。
3、避免多次包含同一个头文件的技术:#ifndef/#endif。仅当以前没有使用预处理器编译指令#define定义一个头文件名称时,才处理#ifndef和#endif之间的语句。
4、链接性描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。自动变量(函数定义中声明的变量(包括函数参数))的名称没有链接性,因为它们不能共享,作用域为局部。
5、常用的管理自动变量的方法是留出一段内存,并将其视为栈,以管理变量的增减。栈的默认长度取决于实现,但编译器通常提供改变栈长度的选项。程序使用两个指针来跟踪栈,一个指向栈底,另一个指向栈顶,即下一个可用的内存单元。函数结束时,栈顶指针被重置为函数被调用前的值,从而释放新变量使用的内存。新变量没有被删除,但不再被标记,它们所占用的空间被下一个将值加入到栈中的函数所使用。
6、C++支持使用register关键字来声明局部变量,说明该变量将被频繁使用。寄存器变量是另一种形式的自动变量。CPU访问寄存器中的值的速度比访问栈中的内存快。但编译器不一定会满足此声明。注:在寄存器中的变量没有内存地址,因此不能将地址操作符用于寄存器变量。
7、C++为静态存储持续性变量提供三种链接性:外部、内部和无链接性。这三种链接性都在整个程序执行期间存在,与自动变量相比,它们的寿命更长。编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在。
8、链接性为外部的静态持续变量:必须在代码块的外面声明它,可以在程序的其他文件中使用它,即在其他文件中用extern对该变量进行引用声明,而且只有一个文件包含了该变量的外部定义;链接性为内部的静态持续变量:必须在代码块的外面声明它,并使用static限定符,表明该变量只能在其所属的文件中使用;无链接性静态持续变量:必须在代码块内声明它,并使用static限定符,这意味着虽然该变量只在该代码块中可用,但它在该代码块不处于活动时仍然存在。例子如下:
...
int global=1000; // 链接性为外部的静态变量
static int one_file=50; // 链接性为内部的静态变量
int main()
{
...
}
void fun1(int n)
{
static int count=0; // 无链接性静态持续变量
}
9、定义与全局变量同名的局部变量后,局部变量将隐藏全局变量。当将(::)放在变量名称前面时,该操作符表示使用变量的全局版本。
10、应使用链接性为外部的静态持续变量在多文件程序的不同部分之间共享数据;应使用链接性为内部的静态持续变量在同一个文件的多个函数之间共享数据。
11、如果初始化了 静态局部变量,则程序只在启动时进行一次初始化。以后再调用函数时,将不会像自动变量那样再次被初始化。
12、volatile关键字表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。它的作用时为了改善编译器的优化能力。如果不将变量声明为volatile,则编译器将进行这种优化;将变量声明为volatile,相当于告诉编译器,不要进行这种优化。
13、mutable用来指出,即使结构(或类)变量为const,其某个成员也可以被修改。
14、默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部的(就像使用了static说明符一样)。如果希望某个常量的链接性为外部的,则可以使用extern来覆盖默认的内部链接性。如:extern const int a=1;
15、C++不允许在一个函数中定义另外一个函数,因此所有函数的存储持续性都自动是静态的,即整个程序执行期间都一直存在。在默认情况下,函数的链接性为外部的,即可以在文件间共享。也可以用static将函数的链接性设置为内部的,使之只能在一个文件中使用,必须同时在原型和函数定义中使用该关键字。
16、对于非内联函数,程序中只能包含一个定义。
17、C语言链接性:在C语言中,一个名称只对应一个函数。C语言编译器可能将fun这样的函数名翻译为_fun。C++语言链接性:C++中,同一个名称可能对应多个函数,必须将这些函数翻译为不同的符号名称。
18、如果在C++程序中使用C库中预编译的函数,为了解决名字的匹配问题,可以用函数原型来指出要使用的命名约定:
extern “C” void fun(); // 使用C语言链接性
extern void fun(); // 使用C++语言链接性
extern “C++” void fun(); // 使用C++语言链接性
19、使用new来设置指针的语句必须位于函数中,这是因为只能使用常量表达式来初始化静态存储变量。
20、布局new操作符:指定要使用的位置。例子:
char buffer[500];
int *p;
p=new (buffer) int[20]; // 从buffer中分配空间给一个包含20个元素的int数组。
注:buffer指定的是静态内存,而delete只能用于这样的指针:指向常规new操作符分配的堆内存。也就是说,数组buffer位于delete的管辖区之外。所以不用delete来释放使用new操作符分配的内存。
21、声明区域:可以在其中进行声明的区域。潜在作用域:从声明点开始,到其声明区域的结尾。因此潜在作用域比声明区域小,这是由于变量必须定义后才能使用。
22、名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。在默认情况下,在名称空间中声明的名称的链接性是外部的(除非它引用了常量)。
23、任何名称空间中的名称都不会与其他名称空间中的名称发生冲突。
24、通过作用域解析操作符(::)来访问名称空间的名称。
25、using声明使特定的标识符可用,using编译指令使整个名称空间可用。
26、注:假设名称空间和声明区域定义了相同的名称。如果试图使用using声明将名称空间的名称导入该声明区域,则这两个名称会发生冲突,从而出错。如果使用using编译指令将名称空间的名称导入该声明区域,则局部版本将隐藏名称空间不版本。
27、一般来说,使用using声明比使用using编译指令更安全,这是由于它只导入指定的名称。如果该名称与局部名称发生冲突,编译器将发出指示。using编译指令导入所有名称,包括可能不需要的名称。如果与局部名称发生冲突,则局部名称将覆盖名称空间的版本,而编译器并不会发出警告。
28、名称空间的其他特性:
①namespace A
{
namespace B
{
int a;
...
}
}
访问a的话:A::B::a。也可以使用using编译指令使内部的名称可用:using namespace A::B;②另外,也可以在名称空间中使用using编译指令和using声明:
namespace myth
{
using Jill::fetch;
using namespace element;
using std::cout;
using std::cin;
}
访问Jill::fetch,可以这样:std::cin>>myth::fetch;也可以这样:std::cout<<Jill::fetch;
using namespace myth;(添加了element名称空间)和using namespace myth;using namespace element;等价。
③可以给名称空间创建别名。namespace my_love{...}; namespace ml=my_love;
29、不能在未命名名称空间所属文件之外的其他文件中,使用该名称空间中的名称,因此这种方法可以替代链接性为内部的静态变量。例如:
static int one_file; // 链接性为内部的静态变量
int main()
{
...
}
可以这样:
namespace
{
int one_file; // 链接性为内部的静态变量
}
int main()
{
...
}
30、在名称空间中声明的函数名的作用域为整个名称空间,因此定义和声明必须位于同一个名称空间中。