C++回顾——名字控制

一、来自C中的静态元素
在C和C++中,static有两种基本含义(这两种含义经常是互相冲突的):
1)在固定的地址上进行存储分配,即对象是在一个特殊的静态数据区上创建的(静态存储);
2)对一个特定的编译单位来说是局部的。

1、函数内部的静态变量
C和C++允许函数内部定义一个static对象,这个对象将存储在程序的静态数据区中,这个对象只在函数第一次调用时初始化一次,以后它将在两次函数调用之间保持它的值。
零赋值只对内部类型有效,用户自定义类型必须用构造函数来初始化。因此,如果在定义一个静态对象时没有指定构造函数参数,这个类就必须有默认的构造函数。
静态对象的析构函数(包括静态存储的所有对象)在程序从main()中退出时,或标准C的exit()被调用时才被调用。多数情况下main()的结尾也是调用exit()来结束程序的,这意味着如果在析构函数内部使用exit()将会导致无穷的递归。但如果用标准C的abort()来退出程序,静态对象的析构函数并不会被调用。
静态对象的销毁也是按照与初始化时相反的顺序进行的。

二、C++中的静态成员
类的静态成员拥有一块单独的存储区,不管创建了多少个该类的对象,所有这些对象的静态数据成员都共享这一块静态存储空间(这就为这些对象提供了一种互相通信的方法),但静态数据属于类,它的名字只在类的范围内有效,可以是public、private、protected。

如果一个静态数据成员被声明但没有定义时,连接器会报告一个错误。定义必须出现在类的外部(不允许内联)而且只能定义一次,因此它通常放在一个类的实现文件中。例如:
class A {
static int param;
public:
// …
};
之后,必须在定义文件中为静态数据成员定义存储区:
int A::param = 1;

静态数据成员可以放在另一个类的嵌套类中,但是不能在局部类(在函数内部定义的类)中有静态数据成员。

当在一个类中看到静态成员函数时,要记住:类的设计者是想把这些函数与整个类在概念上关联起来。静态成员函数不能访问一般的数据成员,只能访问静态数据成员,也只能调用其他的静态成员函数。通常,当前对象的地址是被隐式地传递到被调用的函数的,但是静态成员函数没有this,所以它无法访问一般的成员(非静态成员函数可以访问静态成员函数和变量)。静态函数意味着对这个函数的所有调用来说,一个局部变量只有一份拷贝。

三、静态初始化的相依性
在一个指定的翻译单元中,静态对象的初始化顺序严格按照对象在该单元中定义出现的顺序,而清除的顺序则与初始化的顺序正好相反。但是,对于作用域为多个翻译单元的静态对象来说,不能保证严格的初始化顺序,也没有办法来指定这种顺序。

相互依赖的静态对象的初始化,可能会导致程序异常,有三种方法来处理这一问题:
1)不用它,避免初始化时的相互依赖(最好的解决办法);
2)如果实在要用,就把那些关键的静态对象的定义放在一个文件中,这一只要让它们在文件中顺序正确就可以保证它们正确的初始化;
3)如果确信把静态对象放在几个不同的翻译单元中是不可避免的(如在编写一个库时),通过两种程序设计技术加以解决:A、在库头文件中加上一个额外的类,这个类负责库中的静态对象的动态初始化。B、把一个静态对象放在一个能返回对象引用的函数中(使用这种方法,访问静态对象的唯一途径就是调用这个函数,函数第一次被调用时,它强迫初始化发生。静态初始化的正确顺序是由设计的代码而不是由连接器任意指定顺序来保证的)。

不能把定义了静态对象的函数作为内联函数,这是因为内联函数在它出现的每一个文件中都会有一份副本(这种副本包括静态对象的定义),而且内联函数自动地默认为内部连接,所以这将导致多个重复的静态对象,且它们作用域为多个编译单元。

四、名字空间
C++的名字空间(namespace)特征,把一个全局名字空间分成多个可管控的小空间。创建一个名字空间与创建一个类非常相似:
namespace Mynamespace{
// Declaration
}
注意:
1)namespace只能在全局范围内定义,但它们之间可以互相嵌套;
2)在namespace定义的结尾,右花括号的后面不必跟一个分号;
3)一个namespace可以在多个头文件中用一个标识符来定义,就好像重复定义一个类一样。
4)一个namespace可以用另一个名字来作它的别名(如namespace MN = Mynamespace;);
5)不能像类那样去创建一个名字空间的实例;
6)每个翻译单元都可以包含一个未命名的名字空间(最多只能有一个),在这个空间中的变量和函数自动地在翻译单元内有效(不需要加上static说明就可以让它们作为内部连接);
7)可以在一个名字空间的类定义内插入一个友元声明。

3、使用名字空间
在一个名字空间中引用一个名字可以采取三种方法:
1)用作用域运算符;

2)使用using指令把所有名字引入到名字空间中(使用using指令可能会引入带名字冲突的名字空间);

3)用using声明一次性引用名字(using声明给出了标识符的完整名字,但没有类型方面的信息,即如果名字空间中包含了一组用相同名字重载的函数,using声明就声明了这个重载的集合内的所有函数),可以把using声明放在任何一般的声明可以出现的地方。using声明与普通声明只有一点不同:using声明可以引起一个函数用相同的参数类型来重载(这在一般的重载中是不允许的)。如果不想由于引入不同的名字空间而导致重复定义一个函数时,可以使用using声明,它不会引起任何二义性和重复。

不要把一个全局的using指令引入到一个头文件中,因为那将意味着包含这个头文件的任何其他文件也会打开这个名字空间。

五、替代连接说明
C++中提供了一个替代连接说明,它是通过重载extern关键字来实现的。extern后跟一个字符串来指定想声明的函数的连接类型,后面是函数声明。如:
extern “c” float f(int a, char b);
这就告诉编译器f()是C连接,这样就不会转换函数名。标准的连接类型指定符有“C”和“C++”两种,但编译器开发商可选择用同样的方法支持其他语言。
如果有一组替代连接的声明,可以把它们放在花括号内:
extern “C” {
float f(int a, char b);
double d(int a, char b);
}
或在头文件中:
extern “C”{
#include “header.h”
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值