首页 > 代码库 > C++ Primer 学习笔记_107_特殊工具与技术 --固有的不可移植的特征[上]
C++ Primer 学习笔记_107_特殊工具与技术 --固有的不可移植的特征[上]
特殊工具与技术
--固有的不可移植的特征[上]
C++从 C 语言继承来的不可移植特征:位域和 volatile 限定符。这些特征可使与硬件接口的直接通信更容易。
C++ 还增加了另一个不可移植特征(从 C 语言继承来的):链接指示,它使得可以链接到用其他语言编写的程序。
一.位域
可以声明一种特殊的类数据成员,称为位域,来保存特定的位数。当程序需要将二进制数据传递给另一程序或硬件设备的时候,通常使用位域。
位域在内存中的布局是机器相关的。
位域必须是整型数据类型,可以是 signed 或 unsigned。通过在成员名后面接一个冒号以及指定位数的常量表达式,指出成员是一个位域:
typedef unsigned int Bit; class File { Bit mode:2; Bit modified:1; Bit prot_owner: 3; Bit prot_group: 3; Bit prot_world: 3; }; //win7x64,gcc编译器 cout << sizeof(File) << endl;
mode 位域有两个位,modified 只有一位,其他每个成员有三个位。(如果可能)将类定义体中按相邻次序定义的位域压缩在同一整数的相邻位, 从而提供存储压缩。例如,在前面的声明中,5 个位域将存储在一个首先与位域 mode 关联的unsigned int 中。位是否压缩到整数以及如何压缩与机器有关。
[小心!]
通常最好将位域设为unsigned类型.存储在signed类型中的位域的行为由实现定义.
使用位域
用与类的其他数据成员相同的方式访问位域
void File::write() { modified = 1; //... } void File::close() { if (modified) { //... } }
通常使用内置按位操作符操纵超过一位的位域:
enum {READ = 01,WRITE = 02}; int main() { File myFile; myFile.mode |= READ; if (myFile.mode & READ) cout << "myFile.mode READ is set" << endl; }
定义了位域成员的类通常也定义一组内联函数来测试和设置位域的值.如:
inline int File::isRead() const { return mode & READ; } inline int File::isWrite() const { return mode & WRITE; }
有了这些函数,现在就可以将位域声明为File类的私有成员了.
地址操作符(&)不能应用于位域,所以不可能有引用类位域的指针,位域也不能是类的静态成员。
//附:File类完整定义 class File { public: inline int isRead() const; inline int isWrite() const; inline void setRead(); inline void setWrite(); private: enum {READ = 01,WRITE = 02}; typedef unsigned int Bit; private: Bit mode: 2; Bit modified: 1; Bit prot_owner: 3; Bit prot_group: 3; Bit prot_world: 3; }; inline int File::isRead() const { return mode & READ; } inline int File::isWrite() const { return mode & WRITE; } inline void File::setRead() { mode |= READ; } inline void File::setWrite() { mode |= WRITE; } int main() { File myFile; myFile.setRead(); if (myFile.isRead()) cout << "myFile.mode READ is set" << endl; }
二.volatile限定符
[小心!]
Volatile的确切含义与机器相关,只能通过阅读编译器文档来理解;使用volatile的程序在移植到新的机器或编译器时通常必须改变.
直接处理硬件的程序常具有这样的数据成员,它们的值由程序本身直接控制之外的过程所控制。例如,程序可以包含由系统时钟更新的变量。当可以用编译器的控制或检测之外的方式改变对象值的时候,应该将对象声明为 volatile。关键字 volatile 是给编译器的指示,指出对这样的对象不应该执行优化。
用与 const 限定符相同的方式使用 volatile 限定符。volatile 限定符是一个对类型的附加修饰符:
volatile int display_register; volatile Task *curr_task; volatile int ixa[max_size]; volatile Screen bitmap_buf;
display_register 是 int 类型的 volatile 对象;curr_task 是volatile 对象的指针;ixa 是整数的 volatile 数组,该数组的每个元素都认为是 volatile 的;bitmap_buf 是 volatile Screen 对象,它的每个成员都认为是 volatile 的。
用与定义 const 成员函数相同的方式,类也可以将成员函数定义为volatile,volatile 对象只能调用 volatile 成员函数。
曾经学习过const 限定符与指针的相互作用,volatile 限定符与指针之间也存在同样的相互作用。可以声明 volatile 指针、指向 volatile 对象的指针,以及指向 volatile 对象的 volatile 指针:
volatile int v; int *volatile vip; volatile int *ivp; volatile int *volatile vivp; int *ip = &v; //Error vip = &v; //Error ivp = &v; //OK vivp = &v; //OK
像用 const 一样,只能将 volatile 对象的地址赋给指向 volatile 的指针,或者将指向 volatile 类型的指针复制给指向 volatile 的指针。只有当引用为 volatile 时,我们才可以使用 volatile 对象对引用进行初始化。
合成的复制控制不适用于volatile对象
对待 const 和 volatile 的一个重要区别是,不能使用合成的复制和赋值操作符从 volatile 对象进行初始化或赋值。合成的复制控制成员接受 const 形参,这些形参是对类类型的 const 引用,但是,不能将 volatile 对象传递给普通引用或 const 引用。
如果类希望允许复制 volatile 对象,或者,类希望允许从 volatile 操作数或对 volatile 操作数进行赋值, 它必须定义自己的复制构造函数和/或赋值操作符版本:
class Foo { public: Foo(const volatile Foo &); Foo &operator=(volatile const Foo &); Foo &operator=(const volatile Foo &) volatile; };
通过将复制控制成员的形参定义为 const volatile 引用,我们可以从任何各类的 Foo 对象进行复制或赋值:普通 Foo 对象、const Foo 对象、volatile Foo 对象或 const volatile Foo 对象。
[小心]
虽然可以定义复制控制成员来处理 volatile 对象,但更深入的问题是复制 volatile 对象是否有意义,对该问题的回答与任意特定程序中使用 volatile 的原因密切相关.