C++学习笔记二十六-固有的不可移植的特征
一、概述: 1.编写可以容易从一个机器移到其他机器的低级程序是 C 程序设计语言的一个特点。将程序移到新机器的过程称为“移植”,所以说 C 程序是可移植的。
2.为了支持低级编程,C 语言定义了一些固有不可移植的特征。算术类型的大小随机器不同而变化的事实,就是我们已经遇到过的一个这样的不可移植特征。
3. C++ 的另外两个从 C 语言继承来的不可移植特征:位域和 volatile 限定符。这些特征可使与硬件接口的直接通信更容易。
4.C++ 还增加了另一个不可移植特征(从 C 语言继承来的):链接指示,它使得可以链接到用其他语言编写的程序。
二、位域:
1.声明一种特殊的类数据成员,称为位域,来保存特定的位数。当程序需要将二进制数据传递给另一程序或硬件设备的时候,通常使用位域。
2.位域在内存中的布局是机器相关的。
2. 含有位域数据成员的类的使用,和普通类的使用是一样的,位域也不能是类的静态成员
3.位域必须是整型数据类型,可以是 signed 或 unsigned。通过在成员名后面接一个冒号以及指定位数的常量表达式,指出成员是一个位域
4.过在成员名后面接一个冒号以及指定位数的常量表达式,指出成员是一个位域:
typedef unsigned int Bit; class File { Bit mode: 2; Bit modified: 1; Bit prot_owner: 3; Bit prot_group: 3; Bit prot_world: 3; // ... };
在前面的声明中,5 个位域将存储在一个首先与位域 mode 关联的 unsigned int 中。位是否压缩到整数以及如何压缩与机器有关。
5.一个位域必须存储在同一个字节中,不能跨字节,比如跨两个字节。如果一个字节所剩空间不够存储另一位位域时,应该从下一个字节存放该位域。
6.地址操作符(&)不能应用于位域,所以不可能有引用类位域的指针,位域也不能是类的静态成员。
三、volatile 限定符
1.volatile 的确切含义与机器相关,只能通过阅读编译器文档来理解。使用 volatile 的程序在移到新的机器或编译器时通常必须改变。
2.直接处理硬件的程序常具有这样的数据成员,它们的值由程序本身直接控制之外的过程所控制。例如,程序可以包含由系统时钟更新的变量。当可以用编译器的控制或检测之外的方式改变对象值的时候,应该将对象声明为 volatile。关键字 volatile 是给编译器的指示,指出对这样的对象不应该执行优化。
3.用与 const 限定符相同的方式使用 volatile 限定符。volatile 限定符是一个对类型的附加修饰符:
volatile int display_register; volatile Task *curr_task; volatile int ixa[max_size]; volatile Screen bitmap_buf;4. 用与定义 const 成员函数相同的方式,类也可以将成员函数定义为 volatile,volatile 对象只能调用 volatile 成员函数。
5.volatile 限定符与指针之间也存在同样的相互作用与const 限定符与指针的相互作用相似。可以声明 volatile 指针、指向 volatile 对象的指针,以及指向 volatile 对象的 volatile 指针:
volatile int v; // v is a volatile int int *volatile vip; // vip is a volatile pointer to int volatile int *ivp; // ivp is a pointer to volatile int 6.合成的复制控制不适用于 volatile 对象:对待 const 和 volatile 的一个重要区别是,不能使用合成的复制和赋值操作符从 volatile 对象进行初始化或赋值。合成的复制控制成员接受 const 形参,这些形参是对类类型的 const 引用,但是,不能将 volatile 对象传递给普通引用或 const 引用。
7.如果类希望允许复制 volatile 对象,或者,类希望允许从 volatile 操作数或对 volatile 操作数进行赋值,它必须定义自己的复制构造函数和/或赋值操作符版本:
class Foo { public: Foo(const volatile Foo&); // copy from a volatile object // assign from a volatile object to a non volatile objet Foo& operator=(volatile const Foo&); // assign from a volatile object to a volatile object Foo& operator=(volatile const Foo&) volatile; // remainder of class Foo };
8.通过将复制控制成员的形参定义为 const volatile 引用,我们可以从任何各类的 Foo 对象进行复制或赋值:普通 Foo 对象、const Foo 对象、volatile Foo 对象或 const volatile Foo 对象。
9.虽然可以定义复制控制成员来处理 volatile 对象,但更深入的问题是复制 volatile 对象是否有意义,对该问题的回答与任意特定程序中使用 volatile 的原因密切相关。
10.虽然可以定义复制控制成员来处理 volatile 对象,但更深入的问题是复制 volatile 对象是否有意义,对该问题的回答与任意特定程序中使用 volatile 的原因密切相关。
四、链接指示 extern "C"
1.C++ 程序有时需要调用用其他程序设计语言编写的函数,最常见的一语言是 C 语言。像任何名字一样,必须声明用其他语言编写的函数的名字,该声明必须指定返回类型和形参表。编译器按处理普通 C++ 函数一样的方式检查对外部语言函数的调用,但是,编译器一般必须产生不同的代码来调用用其他语言编写的函数。C++ 使用链接指示指出任意非 C++ 函数所用的语言。
2.链接指示有两种形式:单个的或复合的。链接指示不能出现在类定义或函数定义的内部,它必须出现在函数的第一次声明上。
头文件 cstdlib 中声明的一些 C 函数。该头文件中声明形如:
// illustrative linkage directives that might appear in the C++ header <cstring> // single statement linkage directive extern "C" size_t strlen(const char *); // compound statement linkage directive extern "C" { int strcmp(const char*, const char*); char *strcat(char*, const char*); }
3.链接指示与头文件:可以将多重声明形式应用于整个头文件。例如,C++ 的 cstring 头文件可以像这样:
// compound statement linkage directive extern "C" { #include <string.h> // C functions that manipulate C-style strings }
4.当将 #include 指示在复合链接指示的花括号中的时候,假定头文件中的所有普通函数声明都是用链接指示的语言编写的函数。链接指示可以嵌套,所以,如果头文件包含了带链接指示的函数,该函数的链接不受影响。
五、导出 C++ 函数到其他语言:
1.通过对函数定义使用链接指示,使得用其他语言编写的程序可以使用 C++ 函数:
// the calc function can be called from C programs extern "C" double calc(double dparm) { /* ... */ }
2.当编译器为该函数产生代码的时候,它将产生适合于指定语言的代码。
3.用链接指示定义的函数的每个声明都必须使用相同的链接指示。
4.链接指示支持的语言:支持什么语言随编译器而变。你必须查阅用户指南,获得关于编译器可以提供的任意非 C 链接说明的进一步信息。
六、重载函数与链接指示:
1.链接指示与函数重载之间的相互作用依赖于目标语言。如果语言支持重载函数,则为该语言实现链接指示的编译器很可能也支持 C++ 的这些函数的重载。
2.C++ 保证支持的唯一语言是 C。C 语言不支持函数重载,所以,不应该对下面的情况感到惊讶:在一组重载函数中只能为一个 C 函数指定链接指示。用带给定名字的 C 链接声明多于一个函数是错误的:
// error: two extern "C" functions in set of overloaded functions extern "C" void print(const char*); extern "C" void print(int);
3.在 C++ 程序中,重载 C 函数很常见,但是,重载集合中的其他函数必须都是 C++ 函数:
// the C function can be called from C and C++ programs // the C++ functions overload that function and are callable from C++ extern "C" double calc(double); extern SmallInt calc(const SmallInt&); extern BigNum calc(const BigNum&);
七、extern "C" 函数和指针:
1.编写函数所用的语言是函数类型的一部分。为了声明用其他程序设计语言编写的函数的指针,必须使用链接指示:
// pf points to a C function returning void taking an int extern "C" void (*pf)(int);
使用 pf 调用函数的时候,假定该调用是一个 C 函数调用而编译该函数。
2.C 函数的指针与 C++ 函数的指针具有不同的类型,不能将 C 函数的指针初始化或赋值为 C++ 函数的指针(反之亦然)。 存在这种不匹配的时候,会给出编译时错误:
void (*pf1)(int); // points to a C++ function extern "C" void (*pf2)(int); // points to a C function pf1 = pf2; // error: pf1 and pf2 have different types
3.应用于整个声明的链接指示:使用链接指示的时候,它应用于函数和任何函数指针,作为返回类型或形参类型使用:
// f1 is a C function; its parameter is a pointer to a C function extern "C" void f1(void(*)(int));
这个声明是说,f1 是一个不返回值的 C 函数,它有一个形参,该形参是不返回值并接受单个形参的函数的指针。链接指示应用于该函数指针以及 f1。调用的时候,必须将 C 函数名字或 C 函数指针传递给它。
4.因为链接指示应用于一个声明中的所有函数,所以必须使用类型别名,以便将 C 函数的指针传递给 C++ 函数:
// FC is a pointer to C function extern "C" typedef void FC(int); // f2 is a C++ function with a parameter that is a pointer to a C function void f2(FC *);