C++:66---特殊工具与技术之(不可移植的特性:位域、volatile、extern “C“链接提示)
为了位置底层编程,C++定义了一些固有的不可移植的特性。所谓不可移植的特性是指因机器而异的特性
- 当我们将含有不可移植的程序从一台机器转移到另一台机器上时,通常需要重新编写该程序。算术类型的大小在不同机器上不一样,这是一个典型实例
- 本文介绍的特性有:
- 位域:从C语言继承而来的特性
- volatile:从C语言继承而来的特性
- extern:C++新增的链接指示
一、位域
- 类可以将其(非静态)数据成员定义为位域,在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域
- 取地址运算符(&)不能作用域位域,因此任何指针都无法指向类的位域
位域的定义
- 位域的类型必须是整型或枚举类型
- 因为带符号位域的行为是由具体实现确定的,所以通常情况下使用无符号类型保存一个位域
- 位域在表达式的后面用一个冒号跟一个常量表达式,常量表达式用来指示成员所占的二进制位数
- 例如:
typedef unsigned int Bit; class File {Bit mode : 2; //占2位Bit modified : 1; //占1位 Bit prot_owner : 3; //占3位Bit prot_group : 3; //占3位Bit prot_world : 3; //占3位 public://文件类型以八进制的形式表示enum modes { READ = 01, WRITE = 02, EXECUTE = 03 };File &open(modes);void close();void write();bool isRead()const;void setWrite(); };
- 位域的压缩:
- 如果可能的话,在类的内部连续定义的位域压缩在同一个整型的相邻位,从而提供存储压缩
- 例如在上面的代码中,五个位域可能会存储在同一个unsigned int中
使用位域
- 访问位域的方式与普通的数据成员一样。例如:
void File::write() {modified = 1;//... }void File::close() {if (modified)//... }
- 通常使用内置的位运算符操作超过1位的位域:
File &File::open(File::modes m) {mode |= READ; //按默认方式设置READ//其他处理//如果打开了READ和WRITEif (m&WRITE)return *this; }
- 如果一个类定义了位域成员,则它通常也会定义一组内联的成员函数以检验或设置位域的值:
inline bool File::isRead()const {return mode&READ; }inline void File::setWrite() {mode |= WRITE; }
二、volatile
volatile的使用场景
- 直接处理硬件的程序常常包含这样的元素,它们的值由程序直接控制之外的过程控制
- 例如,程序可能包含一个由系统时钟定时更新的变量。当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为volatile。关键字volatile告诉编译器不应该对这样的对象进行优化
volatile变量的定义
- 下面是一些volatile变量的定义:
volatile int display_register; //该int值可能发生改变 volatile Task *curr_task; //curr_task指向一个volatile对象 volatile int iax[max_size]; //iax的每个元素都是volatile volatile Screen bitmapBuf; //bitmapBuf的每个成员都是volatile
- volatile的成员函数只能被volatile的对象调用
volatile与指针
- volatile也可以与指针在一起使用
- 我们可以声明volatile指针、指向volatile对象的指针、以及指向volatile对象的volatile指针
- 例如:
volatile int v; //v是一个volatile int int *volatile vip; //vip是一个指针,它指向int volatile int *ivp; //ivp是一个指针,它指向一个volatile int volatile int *volatile vivp; //vivip是一个volatile指针,它指向一个volatile int//int *ip = &v; 错误,必须使用volatile的指针 ivp = &v; //正确,ivp是一个指向volatile的指针 vivp = &v; //正确,vivip是一个指向volatile的volatile指针
- 引用的规则类似,只有当某个引用时volatile的时,我们才能使用一个volatile对象初始化该引用
合成的拷贝对volatile对象无效
- 一个重要概念:
- 我们不能使用合成的拷贝/移动构造函数及赋值运算符初始化volatile对象或从volatile对象赋值
- 原因:合成的成员接受的形参类型是(非volatile)常量引用,显然我们不能把一个非volatile引用绑定到一个volatile对象上
- 因此,如果我们想拷贝/移动或赋值它的volatile对象,则该类必须自定义拷贝或移动操作。例如,我们可以将形参类型指定为const volatile引用,这样我们就可以进行拷贝或赋值操作了:
class Foo { public://从一个volatile对象进行拷贝Foo(const volatile Foo&);//将一个volatile对象赋值给一个非vaoltile对象Foo &operator=(volatile const Foo&);//将一个volatile对象赋值给一个vaoltile对象(因为只有volatile对象才能调用这个函数)Foo &operator=(volatile const Foo&)volatile; };
- 尽管我们可以为volatile对象定义拷贝或赋值操作,但是一个更深层次的问题是:拷贝volatile对象是否有意义?这个问题与具体的实现有关
三、extern
- C++程序有时需要调用其他语言编写的函数(C语言为主)。像所有其他名字一样,其他语言中的函数名字也必须在C++中进行声明(并且带有返回类型和形参列表)
- 对于其他语言编写的函数来说,编译器检查其调用的方式与处理普通C++函数的方式相同,但是生成的代码有所区别。C++使用链接指示指出任意非C++函数所用的语言
声明一个非C++的函数
- 链接指示:
- 链接指示可以有两种形式:单个的、复合的
- 链接指示不能出现在类定义或函数定义的内部
- 链接指示格式:
- extern关键字+一个字符串字面值+单个/一组函数的声明
- 字符串字面值:指示出编写函数所用的语言,编译器可以支持对C语言的链接指示(extern "C"),编译器还可能支持Ada、FORTRAN等语言
- 演示案例:
//可能出现在C++头文件<cstring>中的链接指示 //单语句的链接指示 extern "C" size_t strlen(const char *);
//可能出现在C++头文件<cstring>中的链接指示 //复合语句的链接指示 extern "C" {int strcmp(const char*, const char*);char *strcat(char*, const char*); }
链接指示与头文件
- 复合声明的形式可以应用于整个头文件。例如,C++的cstring头文件可能有下面形式的代码:
//复合语句链接指示 extern "C" { #include <string.h> //操作C风格字符串的C函数 }
- 链接指示可以嵌套,如果上面的头文件中也包含有链接指示的函数,则该函数的链接不受影响
指向extern "C"函数的指针
- 指向其他语言编写的函数的指针必须与函数本身使用相同的链接指示
- 例如:
//pf指向一个C函数,该函数接受一个int返回void extern "C" void(*pf)(int);
- 当我们使用pf调用函数时,编译器认定当前调用的是一个C函数
- 指向C函数的指针与指向C++函数的指针是不一样的类型:
- 一个指向C函数的指针不能用在执行初始化或赋值操作后指向C++函数
- 反之亦然
- 就像其他类型不匹配的问题一样,如果我们试图在两个链接指示的指针之间进行赋值操作,会产生错误。例如:
void(*pf1)(int); //指向一个C++函数 extern "C" void(*pf2)(int); //指向一个C函数pf1 = pf2; //错误,pf1和pf2的类型不同
链接指示对整个生命都有效
- 当我们使用链接指示时,它不仅对函数有效,而且对作为返回类型和形参类型的函数指针也有效。例如:
//f1是一个C函数,它的形参是一个指向C函数的指针 extern "C" void f1(void(*)(int));
- 当我们调用上面的f1函数时,必须传递给它一个C函数的名字或者指向C函数的指针
- 因为链接指示同时作用于声明语句中的所有函数,所以如果我们希望给C++函数传入一个指向C函数的指针,则必须使用类型别名。例如:
//FC是一个指向C函数的指针 extern "C" typedef void FC(int);//f2是一个C++函数,该函数的形参是指向C函数的指针 void f2(FC *);
导出C++函数到其他语言
- 通过使用链接指示对函数进行定义,我们可以令一个C++函数在其他语言编写的程序中可用。例如:
//calc函数可以被C程序调用 extern "C" double cal(double dparm) {//... }
- 编译器将为calc函数生成适合于指定语言的代码
- 值得注意的是:
- 可被多种语言共享的函数的返回类型或形参类型受到很多限制
- 例如,我们不太可能把一个C++类的对象传给C程序,因为C程序根本无法理解构造函数、析构函数以及其他类特有的操作
重载函数与链接指示
链接指示与重载函数的相互作用依赖于目标语言。如果目标语言支持重载函数,则为该函数实现链接指示的编译器很可能也支持重载这些C++的函数
C语言不支持函数重载,因此也就不难理解为什么一个C链接指示只能用于说明一组重载函数中的某一个了。例如:
//错误,两个extern "C"函数的名字相同 extern "C" void print(const char*); extern "C" void print(int);
- 如果在一组重载函数中有一个是C函数,则其余的必定都是C++函数。例如:
class SmallInt {}; class BigNum {};//C函数可以在C/C++程序中使用 //C++函数重载了该函数,可以在C++程序中调用 extern "C" double calc(double); extern SmallInt calc(const SmallInt&); extern BigNum calc(const BigNum&);
- C版本的calc函数可以在C或C++程序中调用,而使用了类类型形参的C++函数只能在C++程序中调用。上述性质与声明的顺序无关
C++:66---特殊工具与技术之(不可移植的特性:位域、volatile、extern “C“链接提示)相关推荐
- C++ Primer 5th笔记(chap 19 特殊工具与技术)两种不可移植的特性之“volatile”
1. volatile 限定符 当对象的值可能在程序的控制或检测之外被改变时, 应该将该对象声明为 volatile volatile int display_register; //display_ ...
- C++ Primer 5th笔记(chap 19 特殊工具与技术)两种不可移植的特性之“位域”
两种不可移植的特性: 位域和 volatile 1. 位域 ( bit-field ) 类可以将其( 非静态) 数据成员定义成位域 , 在一个位域中含有一定数量的二进制位. 位域的类型必须是整型或枚举 ...
- 第19章 特殊工具与技术【C++】
第19章 特殊工具与技术 到此你会感觉C++越来越离谱,不好好想着解决问题,语法与特性先成为了一大问题.只能说太复杂了,上手难度较高. 本章分别从,控制内存分配.运行时类型识别.枚举类型.类成员指针. ...
- C++_Primer_学习笔记_第十九章(特殊工具和技术)
第十九章(特殊工具与技术) /1.控制内存分配 1).不能直接应用标准内存管理机制. 某一些应用程序需要自定义内存分配的的细节,比如使用关键字new将对象放置在特定的内存空间中. 为了实现这一个目的, ...
- C++Primer5th 第十九章 特殊工具与技术
第十九章 特殊工具与技术 19.1 控制内存分配 19.1.1 重载new和delete malloc函数与free函数 19.1.2 定位new表达式 19.2 运行时类型识别 19.2.1 dyn ...
- C++ Primer 学习笔记 第十九章 特殊工具与技术
某些应用程序对内存分配有特殊需求,如使用new将对象放置在特定的内存空间中,为实现它,应用程序需要重载new和delete. new实际执行步骤: 1.new表达式调用operator new(或op ...
- xmm1是什么器件_模拟电子技术multisim仿真1二极管特性仿真.ppt
模拟电子技术multisim仿真1二极管特性仿真 第6章 模拟电子技术Multisim仿真实验 6.1 二极管特性仿真实验 1.实验要求与目的 (1) 测量二极管的伏安特性,掌握二极管各工作区的特点. ...
- 37、C++ Primer 4th笔记,特殊工具与技术,类成员指针
1.成员指针(pointer to member)包含类的类型以及成员的类型.成员指针只应用于类的非static成员.static类成员不是任何对象的组成部分,所以不需要特殊语法来指向static成员 ...
- C++ Primer 5th笔记(chap 19 特殊工具与技术)异常类层次
1. 类 exception . bad_cast 和 bad_alloc 定 义 了 默 认 构 造 函 数 runtime_error 和 logic_error没有默认构造函数, 但是有一个可以 ...
最新文章
- [原创]SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT 'OpenRowset/OpenDatasource' 的访问...
- BAT可真拿抖音一点儿办法也没有
- 读书笔记_unity4.x第十二章_3D数学基础
- python省市区三级联动_Django Admin实现三级联动的示例代码(省市区)
- (转)base64编码(严格说来,base64不算作加解密算法)
- LeetCode第五天
- 这 10 个云计算错误,会让你的业务一蹶不振!
- 【clickhouse】clickhouse 表引擎 之 AggregatingMergeTree
- js使用const的好处_let和const命令
- 10 个 Python 项目简单又超有趣
- 【H3C模拟器】华三交换机配置IRF堆叠
- 别只关注M1芯片的苹果笔记本,iOS APP支持在 Mac上运行也是大事
- 灰度图像--形态学处理:腐蚀、膨胀、开操作、闭操作、顶帽、底帽等
- 中缀向后缀转换表达式
- 星际反作弊2.3for-win7-xp-win8-win10
- 以太网使用的CSMA/CD协议是以争用方式接入到共享信道的。这与传统的时分复用TDM相比优缺点如何?网络适配器的作用是什么?网络适配器工作在哪一层?假定1km长的CSMA/CD网络的数据率为1Gb/s
- 改变人生的不是道理,而是习惯!(深度好文)
- 几率波量子雷达/反事实量子通信
- Mac下安装Kali Linux 2020.1 (注意默认用户名密码不再是root toor)
- Python使用及交互方式
热门文章
- 课外时间学点历史知识~
- html字体如何运用在ps上,PS新手怎么运用好工作中文本工具
- Java中的基本数据类型讲解(数值型、字符型、布尔型)
- 102.(leaflet篇)leaflet态势标绘-分队战斗行动修改 (燕尾)
- linux系统键盘被锁定,Unix系统键盘失效等问题讲解
- ODrive AP调试记录
- memcached win64位服务端安装和java客户端实例
- Harbor镜像库搭建以及如何在idea上构建镜像并推送Harbor
- W801单片机学习笔记——调试器的配置与使用(适用于W801和W806)
- SQLite.Interop.dll 没有拷贝到输出目录