一.相关知识点
那些通常放在头文件里的名字,像常量、内联函数(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