一.相关知识点
那些通常放在头文件里的名字,像常量、内联函数(inline function),在缺省情况下都是内部连接的(当然常量只有在C + +中缺省情况下是内部连接的,在 C中它缺省为外部连接)。注意连接只引用那些在连接/装载期间有地址的成员,因此类声明和局部变量并没有连接。
名字空间的产生与一个类的产生非常相似:
namespace MyLib{
//Declarations
}
这就产生了一个新的名字空间,其中包含了各种声明.namespace与class、struct、union和enum有着明显的区别:
1) namespace只能在全局范畴定义,但它们之间可以互相嵌套。
2) 在namespace定义的结尾,右大括号的后面不必要跟一个分号。
3) 一个namespace可以在多个头文件中用一个标识符来定义,就好象重复定义一个类一样。
4) 一个namespace的名字可以用另一个名字来作它的别名,这样我们就不必敲打那些开发商提供的冗长的名字了。
5) 我们不能像类那样去创建一个名字空间的实例。
1. 未命名的名字空间
每个编译单元都可包含一个未命名的名字空间—在namespace关键字后面没有标识符。
在编译单元内,这个空间中的名字自动而无限制地有效。每个编译单元要确保只有一个未命名的名字空间。如果把一个局部名字放在一个未命名的名字空间中,无需加上 static说明就可以让它们作内部连接。
2. 友元
可以在一个名字空间的类定义之内插入一个 friend 声明:
namespace me{
class us{
//...
friend you();
};
}
使用名字空间
可以用两种方法在一个名字空间引用同一个名字:一种是用范围分解运算符,还有一种是用using关键字。
1. 范围分解
名字空间中的任何命名都可以用范围分解运算符明确指定,就像引用一个类中的名字一样.
2. using指令
用using 关键字可以让我们立即输入整个名字空间,摆脱输入一个名字空间中标识符的烦恼。这种using和namespace关键字的搭配使用叫作 using 指令。 using 关键字在当前范围内直接声明了名字空间中的所有的名字,所以可以很方便地使用这些无限制的名字.
现在可以在函数内部声明m a t h中的所有名字,但允许这些名字嵌套在函数中。
using 指令有一个缺点,那就是看起来不那么直观,using指令引入名字可见性的范围是在创建using的地方。但我们可以使来自 using 指令的名字暂时无效,就像它们已经被声明为这个范围的全局名一样。
如果有第二个名字空间
这个名字空间也用 using指令来引入,就可能产生冲突。这种二义性出现在名字的使用时,而不是在using指令使用时。
这样,即使永远不产生二义性,写 using指令引入带名字冲突的名字空间也是可能的。
3. using声明
可以用using声明一次性引入名字到当前范围内。这种方法不像 using指令那样把那些名字当成当前范围的全局名来看待,而是在当前范围之内进行一个声明,这就意味着在这个范围内它可以废弃来自using指令的名字。
using声明给出了标识符的完整的名字,但没有了类型方面的信息。也就是说,如果名字空间中包含了一组用相同名字重载的函数, using声明就声明了这个重载的集合内的所有函数。可以把using声明放在任何一般的声明可以出现的地方。 using声明与普通声明只有一点不同: using声明可以引起一个函数用相同的参数类型来重载(这在一般的重载中是不允许的)。当然这种不确定性要到使用时才表现出来,而不是在声明时。using声明也可以出现在一个名字空间内,其作用与在其他地方时一样:
转换连接指定
如果C++中编写一个程序需要用到C库,那该怎么办呢?如果这样声明一个 C函数:
float f(int a,char b);
C++的编译器就会将这个名字变成像 _f_int_int之类的东西以支持函数重载(和类型安全连接)。然而, C编译器编译的库一般不做这样的转换,所以它的内部名为 _f。这样,连接器将无法解决我们C++对f()的调用。
C++中提供了一个连接转换指定,它是通过重载 extern关键字来实现的。 extern后跟一个字符串来指定我们想声明的函数的连接类型,后面是函数声明。
extern "C" float f(int a,char b);
这就告诉编译器 f()是C连接,这样就不会转换函数名。标准的连接类型指定符有“ C”和“ C++”两种,但编译器开发商可选择用同样的方法支持其他语言。
如果我们有一组转换连接的声明,可以把它们放在花括号内:
或在头文件中:
多数C++编译器开发商在他们的头文件中处理转换连接指定,包括 C和C++,所以我们不用担心它们。
虽然标准的C++只支持“C”和“C++”两种连接转换指定,但用同样的方法可以实现对其他语言的支持。
二.相关代码
1.
<span style="font-size:18px;"><strong>/*这是预处理器仍然有用的另一个例子,因为 _ FILE_和_LINE_指示仅和预处理器一 起起作用并用在assert( )宏里。假如assert( )宏在一个错误函数里被调用,它仅打 印出错函数的行号和文件名字而不是调用错误函数。这儿显示了使用宏联接(许多是 assert()方法)函数的方法,紧接着调用assert()(程序调试成功后这由一个#define NDEBUG消除)。*/ /*ALLEGE.h*/ #ifndef ALLEGE_H_ #define ALLEGE_H_ #include <stdio.h> #include <stdlib.h> #include <assert.h> inline void allege_error(int val, const char* msg) //函数allege_error()有两个参数:一个是整型表达式的值,另一个是这个值为 //false时需打印的消息 { if(!val) { fprintf(stderr, "error: %s\n", msg); //函数fprintf()代替iostreams是因为在只有少量错误的情况下,它工作得 //更好。假如这不是为调试建立的,exit(1)被调用以终止程序。 #ifdef NDEBUG exit(1); #endif } } //allege()宏使用三重if-then-else强迫计算表达式expr求值。在宏里调用了 //allege_error(),接着是assert(),所以我们能在调试时获得 assert()的好处 //——因为有些环境紧密地把调试器和assert()结合在一起。 #define allege(expr, msg){ allege_error((expr)?1:0, msg); assert(expr);} #define allegemem(expr) allege(expr, "out of memory") //allegefile( )宏是allege( )宏用于检查文件的专用版本 #define allegefile(expr) allege(expr, "could not open file") //allegemen()宏是allege( )宏用于检查内存的专用版本 #endif </strong></span>
<span style="font-size:18px;"><strong>#include <iostream> #include "ALLEGE.h" using namespace std; char onechar(const char* string = 0) { static const char* s; if(string) { s = string; return *s; } else { allege(s, "un-initialized s"); } if(*s == '\0') { return 0; } return *s++; } char* a = "abcdefghijklmnopqrstuvwxyz"; int main() { onechar(a); char c; while((c = onechar()) != '\0') { cout << c << endl; } return 0; } </strong></span>
<span style="font-size:18px;"><strong>/*FUNOBJ.cpp*/ #include <iostream> using namespace std; //在函数f ( )内部定义一个静态的X类型的对象,它可以用带参数的构造函数来初始化,也可以用 //缺省构造函数。程序控制第一次转到对象的定义点时,而且只有第一次时,才需要执行构造函数。 class X { int i; public: X(int I = 0):i(I) {} ~X() { cout << "X::~X()" << endl; } }; void f() { static X x1(47); static X x2; } int main() { f(); return 0; }</strong></span>
<span style="font-size:18px;"><strong>/*静态对象的析构函数*/ /*STATDEST.cpp*/ #include <fstream.h> ofstream out("statdest.out"); class obj { char c; public: obj(char C):c(C) { out << "obj::obj() for" << c << endl; } ~obj() { out << "obj::~obj() for" << c << endl; } }; obj A('A'); void f() { static obj B('B'); } void g() { static obj C('C'); } int main() { out << "inside main()" <<endl; f(); g(); out << "leaving main()" << endl; }</strong></span>
<span style="font-size:18px;"><strong>/*一个静态成员的初始化表达式是在一个类的范围内*/ /*STATINIT.cpp*/ #include <iostream> using namespace std; //withStatic::限定符把withStatic的范围扩展到全部定义 int x = 100; class withStatic { static int x; static int y; public: void print() const { cout << "withStatic::x = " << x << endl; cout << "withStatic::y = " << y << endl; } }; int withStatic::x = 1; int withStatic::y = x + 1; int main() { withStatic WS; WS.print(); return 0; }</strong></span>
<span style="font-size:18px;"><strong>/*静态数组*/ /*STATARRY.cpp*/ /*对所有的静态数据成员,我们必须提供一个单一的外部定义。这些定义必须有内部 连接,所以可以放在头文件中。初始化静态数组的方法与其他集合类型的初始化一样, 但不能用自动计数。除此之外,在类定义结束时,编译器必须知道足够的类信息来创 建对象,包括所有成员的精确大小。*/ #include <iostream> using namespace std; class Values { static const int size; static const float table[4]; static const letters[10]; }; const int Values::size = 100; const float Values::table[4] = { 1.1, 2.2, 3.3, 4.4 }; const Values::letters[10] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; int main() { return 0; } </strong></span>
<span style="font-size:18px;"><strong>/*嵌套类和局部类 可以很容易地把一个静态数据成员放在一个嵌套内中。然而在局部类(在函数内部定 义的类)中不能有静态数据成员*/ /*LOCAL.cpp*/ #include <iostream> using namespace std; class outer { class inner { static int i;//OK }; }; int outer::inner::i = 47; void f() { class foo { public: //!static int i; }x; } int main() { return 0; }</strong></span>
<span style="font-size:18px;"><strong>/*静态数据成员和静态成员函数在一起使用*/ /*SFUNC.cpp*/ #include <iostream> using namespace std; //因为静态成员函数没有 this指针,所以它不能访问非静态的数据成员,也不能调 //用非静态的成员函数,这些函数要用到this指针)。 class X { int i; static int j; public: X(int I = 0):i(I) { j = i; } int val() const { return i; } static int incr() { //!i++:static member function return +j; } static int f() { //!val(); return incr(); } }; int X::j = 0; int main() { X x; X* xp = &x; x.f(); xp->f(); X::f(); return 0; }</strong></span>
<span style="font-size:18px;"><strong>/*SELFMEN.cpp*/ #include <iostream> using namespace std; class egg { static egg E; int i; egg(int I):i(I){ } public: static egg* instance() { return &E; } int val() { return i; } }; egg egg::E = 47; int main() { //!egg x(1); cout << egg::instance()->val() << endl; return 0; }</strong></span>
<span style="font-size:18px;"><strong>/*DEPEND.h*/ #ifndef DEPEND_H_ #define DEPEND_H_ #include <iostream> using namespace std; extern int x; extern int y; class initializer { static int init_count; public: initializer() { cout << "initializer()" << endl; if(init_count++ == 0) { cout << "performing initialization" << endl; x = 100; y = 200; } } ~initializer() { cout << "~initializer()" << endl; if(--init_count == 0) { cout << "performing cleanup" << endl; } } }; static initializer init; #endif</strong></span>
<span style="font-size:18px;"><strong>/*DEPDEFS.cpp*/ #include "depend.h" int x; int y; int initializer::init_count;</strong></span>
<span style="font-size:18px;"><strong>/*DEPEND.cpp*/ #include "depend.h" int main() { cout << "inside main()" << endl; cout << "leaving main()" << endl; return 0; }</strong></span>
三.习题+解答
1. 创建一个带整型数组的类。在类内部用未标识的枚举变量来设置数组的长度。增加一个const int 变量,并在构造函数初始化表达式表中初始化。增加一个 static int 成员变量并用特定值来初始化。增加一个内联(inline)构造函数和一个内联(inline)型的print()函数来显示数组中的全部值,并在这两函数内调用静态成员函数。
#include <iostream> using namespace std; class A { const int i; static int j; enum{ SIZE = 100 }; int arr[SIZE]; public: A(int I = 0):i(I) { for(int k = 0; k < SIZE; ++k) { arr[k] = incr(); } }; void print() { for(int k = 0; k < SIZE; ++k) { cout << arr[k] << " "; } cout << endl << endl; cout << "j = " << incr() << endl; } static int incr() { ++j; return j; } }; int A::j = 47; int main() { A a; a.print(); return 0; }
2. STATDEST.CPP中,在main()内用不同的顺序调用f()、g()来检验构造函数与析构函数的调用顺序,我们的编译器能正确地编译它们吗?
修改代码使其在窗口显示
#include <fstream.h> ofstream out("statdest.out"); class obj { char c; public: obj(char C):c(C) { cout << "obj::obj() for" << c << endl; } ~obj() { cout << "obj::~obj() for" << c << endl; } }; obj A('A'); void f() { static obj B('B'); } void g() { static obj C('C'); } int main() { cout << "inside main()" <<endl; g(); f(); cout << "leaving main()" << endl; }结果分析:
(1).
(2).
编译器可以正确编译。
3. 在STATDEST.CPP中,把out的定义变为一个extern声明,并把实际定义放到 A(它的构造函数obj传送信息给out)的定义之后,测试我们的机器是怎样进行缺省错误处理的。当我们运行程序时确保没有其他重要程序在运行,否则我们的机器会出现错误。
程序中断,但是编译连接没有问题。
#include <fstream.h> extern ofstream out; class obj { char c; public: obj(char C):c(C) { out << "obj::obj() for" << c << endl; } ~obj() { out << "obj::~obj() for" << c << endl; } }; obj A('A'); ofstream out("statdest.out"); void f() { static obj B('B'); } void g() { static obj C('C'); } int main() { out << "inside main()" <<endl; g(); f(); out << "leaving main()" << endl; }
#include <iostream> using namespace std; class B { static int i; public: B() { cout << "B::B()" << endl; } ~B() { cout << "B::~B()" << endl; exit(0); } }; int B::i = 47; int main() { B b; return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
《C++编程思想》 第九章 命 名 控 制 (知识点+习题+解答)
原文地址:http://blog.csdn.net/qaz3171210/article/details/47205047