c++ 编程拾贝(三)
第10章 名字控制
静态对象的析构函数
是从main()中退出时,或者从c的exit()库函数被调用时才被调用(多数情况下,mian()函数的结尾也是通过调用exit()函数来退出main()程序的)
而这里有个潜在的风险,也即如果在静态函数中加入了exit()函数,会发生无穷地递归调用。这里,如果调用c库函数的abort()来退出程序,静态析构函数不被调用
abort():
异常终止某一个进程,关闭所有的流
这里还提到一个atexit()函数,用来指定main()函数退出前,exit()函数调用前应执行的操作。
atexit()
用来注册函数,在main()调用exit()之后,由exit()函数调用
控制连接
外部连接
:文件作用域文件间是互相可见的,但是局部函数内、类中的名字不算在文件作用域中
对于只想让本文件作用域内的函数或者类使用,而不想被其他文件访问,可以申明成static
,static
只能内部连接,具有文件隔离性质,其他文件单元作用域
可以有同名的定义而不冲突。
注意,这里const在c++中也是默认的内部连接的,而在c中则是外部连接的。
连接只涉及到那些在连接/装载期有地址
的成员(const在c++中默认是无地址的)因此类声明和局部变量并不连接(它们都是运行时分配内存)。
关于extern,static
这里涉及到一个冲突的问题,我们先看这样的在作用域
中的定义:
int a = 0;
int main()
{
.......
}
这里的a隐式地被声明为静态存储的,存储在静态区,而想要其他编译单元可见,则只能这样定义它:
extern int a = 0;
而不管内部连接、外部连接,只要在全局区域定义的变量,都会在连接期之前确定内存区域(静态内存区)
而一旦进入了局部变量static
就不会再改变变量的可见性,只改变变量的存储类型。
如果把局部变量声明为extern
,这意味着告诉编译器放行,该变量意境存在在一个存储区(对于包含该局部变量的函数来说该变量是全局的),举个例子:
#include<iostream>
using namespace std;
int main()
{
extern int i;
cout<<i<<endl;
}
int i = 5;
而对于给函数名添加static
或者extern
属性,只会改动该函数的可见性。我们来看两个例子
extern void f(); //******1
static void p(); //******2
对于f()
来说声明为extern
与没有extern是一样的,而对于p()
来说,则该函数只能在本文件内使用。
名称空间
创建一个名称空间:
namespace Mylib
{
//declaration
}
这里谈一谈namespace的注意点
- namespace 只能在全局区域定义,允许嵌套namespace
- namespace 跟类、结构体、enum不同的地方就是它不用在括号后面跟上分号
- 定义可以在不同文件中重定义
- 也可以取一个别名,使用方法:
namespace BobsSuperDuperLibrary
{
class Widget{};
class Poppit{};
}
//alias
namespace Bob = BobsSuperDuperLibrary;
int main()
{
}
- 这里不能像类那样去实例化一个名称空间实例
- 名称空间允许一个友元,将友元函数放入该名称空间
至于名称空间的使用,使用using或者域作用符即可。
这里的using可以理解为将using后面的名称空间内的所有内容引用到当前名称空间,嵌套在当前名称空间。
甚至可以在函数内部引用名称空间。
using带来的一个可能的问题是二义性的问题,发生二义性是发生在使用名称的时候,而不是在using的时候。这其实就是一个很难发现的潜在问题。
还有一个有意思的是,如果名称空间里有重载函数
//File A
namespace FooSpace
{
void foo();
int foo(int);
}
//File B
int main()
{
using namespace FooSpace::foo;
}
如果在另外的文件里声明引用里FooSpace里的foo函数,这里会将所有的重载函数同时引进。
在这里有一个建议是,using namespace放在cpp文件里,而不要放在一个头文件里,因为引用该头文件时可能会造成名称空间的污染。