const 关键字——常量
const 与define
define是预编译器的编译指令,它从C语言兼容下来,工作方式与文本编辑器中的全局搜索和替换相似。define定义的常量的意义在它开始的地方持续到文件结束,在预编译阶段,预编译器已经将所有define删除,并展开所有的宏定义。它单纯只做文本替换,没有类型安全检查,define命令会很容易引入错误,并且这种错误很难发觉,因此C++中用const取代define预编译指令。另外很多人在学习Python的过程中,往往因为没有好的教程或者没人指导从而导致自己容易放弃,为此我建了个C++交流.裙 :九起久伞吧起伞留伞(数字的谐音)转换下可以找到了,里面有最新C++教程项目可拿,不懂的问题多跟里面的人交流,都会解决哦!
#define SIGMOID(x) (1/(1+exp(-x)))
result = SIGMOID(a+b);//没有正确添加括号,会导致错误结果
result = SIGMOID((a+b));//正确
头文件里的const
const默认是内部链接,如果它被放在头文件中,目的是为了让所有包含它的编译单元能使用这个值,而且是仅让包含头文件的编译单元可见。定义一个const时,必须初始化,除非用extern做出了外部引用。通常,C++编译器不会为const变量分配空间,但extern关键字会强制编译器为const变量分配存储空间。因为extern为外部链接,为了其他的编译单元都能引用到const变量。变量必须要有存储空间。
注:由于编译器不能避免为const分配内存,所以const定义必须默认内部链接。在C++中,const常量是否被分配空间依赖于它如何被使用:对于基本数据类型的常量,编译器会把它放到目标文件的符号表中而不分配存储空间,而自定义的const对象则需要分配存储空间(大对象)。还有一些情况下也需要分配存储空间,例如强制声明为extern或取一个const的地址等操作。
const关键字与多线程安全
可重入是并发安全的保障,一个可重入的函数(函数没有执行完成,由于外部因素或内部调用,又一次进入函数执行)在多线程的环境下可以放心使用。而为了保证一个函数是可重入的,它必须使用任何(局部)静态或非const全局变量。同时,不能返回任何(局部)静态或非const全局变量的指针。
类中的const
在类中定义的非静态const变量,这个类的不同的对象可以含有一个不同的值。const的初始化在类的构造函数的初始化列表中。
static const——编译期间类里的专属常量
static意味着“不管类的对象被创建多少次,都只有一个实例”。必须在static const定义的地方对它初始化。C++用它来代替enum(枚举型),来指示对象的共同属性。它是为整个类服务,而不是某个对象,所以它不能使用this指针(this指针是对成员函数调用时用来指示调用对象的),也不能在复制构造函数中被复制。如果你取某个类的专属常量的地址或编译器坚持要看到一个定义式,则用域名解析符定义一下
//GamePlayer.h中
class GamePlayer{
private:
static const int NumTurns = 5;
int scores[NumTurns];
};
//GamePlayer.cpp中
const int GamePlayer::NumTurns;//NumTurns在class声明中已经初始化,因此这里不再设初值
const对象和成员函数
不修改数据成员的任何函数都应该声明为const,这样它可以和const对象一起使用。
按位const:对象中的每个字节都不能变。
按逻辑const:可以以成员为单位改变。
两种实现按逻辑const的方法
一种是取this指针,并把它强制转换成指向当前类型对象的指针,具体来说就是讲将this强制转换成普通指针。
另一种是使用关键字mutable,以指定一个特定的数据成员可以在一个const对象中被改变。
static关键字——静态区、内部链接
static关键字有两个作用,一个让变量存在静态区,另一个是让错误限制在一个源文件内。让局部变量声明成static使局部变量存储在静态区,从而在程序的整个生命期都存在。同时,当static作用于全局变量时,该全局变量变为内部链接,它的意思是“在文件的外部不可以使用这个名字”,从而使错误局部化。
static全局变量与全局变量的区别是静态变量只初始化一次,防止在其他文件中被引用;static局部变量与普通局部变量的区别除了值在启动程序时初始化一次外,就是它的值在程序的整个周期内都存在,两次函数调用期间,它的值保持不变;局部静态变量在函数调用之间的值保持不变,根据这个特性,可以用于记录函数调用或类创建的一些信息。
static函数与普通函数的区别是static函数在内存中只有一份,而普通函数在每次被调用都维持一份拷贝。
extern关键字——外部变量
extern关键字用来声明另一个文件中的全局变量。所以extern和static是矛盾的,不能同时使用。
extern “C”关键字
C++编译器将extern “C”中的代码单做C语言代码处理。extern C大括号所包围的范围中,C++的名称修饰机制不起作用,对于Visual C++,直接在变量名和函数名前加”_”。但对于Linux下的GCC,extern “C”后面的符号都是修饰后符号。
注:在C语言中不支持使用extern关键字
这里列举我平时编程时遇到的问题及处理方案
在我用vs2012调用C语言写的库时,出现“error LNK2019: 无法解析的外部符号 clGetPlatformIDs“的错误。
原因通常是没有包含相应的lib,也就是说链接器没搜索到相应lib中的clGetPlatformIDs目标模块,基于这种情况,我思考会不会是这个函数在头文件中是C语言函数的声明与定义,但是我又在C++代码中包含该头文件,导致其采用C++的名称修饰机制而无法与C语言库中符号链接。所以对于C++调用C库,需要添加extern “C”关键字声明。
通常的做法是定义宏
#ifdef __cplusplus
extern C" {
#endif
cl_int clGetPlatformIDs(...);
#ifdef _cplusplus
}
#endif
但是如果这个C源码已经编译成库,但是模块的头文件中没有包含extern “C”,则在C++文件中,需要添加
extern "C"{
#include "cl.h"
这相当于在cl.h头文件中所有的声明都添加了extern。
链接与const、static、extern——内存分配与读写
无链接性:在代码块中的局部变量(包括static局部变量)
外部链接:函数之外定义的所有变量(除了const变量)和函数默认为外部链接性。在定义时使用extern关键字显式指定标识符具有外部链接。也就是说在多个文件程序中,可以在文件并且只能在一个文件中定义全局变量,其他文件要使用该变量,要在变量前添加extern关键字。当有extern时,只是告知编译器存在这个变量,编译器并不为该变量分配存储空间,即真正的声明;若没有extern,则在声明的同时,编译器也为该变量分配存储空间。
内部链接:全局static变量和const变量为内部链接,为了使const具有外部链接以便让另外一个文件可以对它引用,必须在当前文件里明确把它定义为extern并初始化。
应使用链接性为外部的多文件程序的不同文件中共享数据,而使用链接性为内部的静态变量在同一文件中的多个函数间共享数据
volatile关键字
告诉编译器“该变量不知道何时回改变“,防止编译器依据变量的稳定性(短期内值不变)作任何优化。
volatile关键字与多度优化
在多线程环境下,即使合理地使用了锁,也不一定能保证线程安全。
x=y=0
Thread1 Thread2
x=1 y=1
r1=y r2=x
由于CPU的动态调度或编译器的优化,执行程序时,有可能交换两条毫不相干的相邻指令的顺序,导致r1=r2=0的情况的发生。而volatile关键字可以试图阻止优化。
(1)阻止编译器为了提高速度将一个变量缓存到寄存器而不写回。也就是说防止编译器根据变量的稳定性作任何优化,假设要读一个硬件中的寄存器,将使用这个关键字。无论何时需要volatile变量的值,编译器都要硬着头皮头读,即使该行之前刚刚读过。
(2)阻止编译器调整volatile变量的指令顺序。(这一步其实只能阻止编译器的优化换序,并不能 阻止CPU动态调度换序)
注:阻止CPU乱序执行的唯一办法是调用CPU提供barrier指令;
关键字restrict
关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。
最后注意一点,restrict是C99中定义的关键字,C++目前并未引入;在GCC可通过使用参数” -std=c99”
来开启对C99的支持
enum枚举类型
本质上,enum是一个int型,但C++不允许enum到int的隐式转换。枚举的类型名也是可选的。enum{ a,b,c}choice;可以立即定义一个enum实例。一个类中的枚举在编译期间分配值,不占用对象的内存空间。但通常优先选择static const而尽量不是用enum。枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数
register变量
关键字register只是告诉编译器“尽可能快地访问该变量”dawn并不能保证将变量放置在寄存器中。同时,register有许多限制,比如不能获取register变量的地址,不能将其声明为全局或静态变量,因为register变量没有内存地址。因此,最好避免使用。
————————————————
另外很多人在学习Python的过程中,往往因为没有好的教程或者没人指导从而导致自己容易放弃,为此我建了个C++交流.裙 :九起久伞吧起伞留伞(数字的谐音)转换下可以找到了,里面有最新C++教程项目可拿,不懂的问题多跟里面的人交流,都会解决哦!
以上,有些内容转自网络,如有侵权,请联系博主改正。