各种C++关键字的作用
一. static
static关键字至少有下列5个作用:
(1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
以下是c++对static的扩展
(4)在类中的static成员变量意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
在linux的模块中,为了避免不同模块的函数名和变量名出现重名,所有的函数和全局变量都要用static关键字声明,将其作用域限制在本模块内部。
二.const
1.修饰常量(const常量)
const常量,如const int max = 100;
优点:const常量有数据类型,而(define)宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误 .
另外,const定义常量从汇编的角度来看,只是给出了对应的内存地址,而#define给出的是立即数,所以,const常量在程序运行过程中只有一份拷贝,而#define常量在内存中有若干个拷贝,可以节省空间。
2.修饰指针
int b = 500; const int* a = &b; //情况一 int const* a = &b; //情况二 int* const a = &b; //情况三 const int* const a = &b; //情况四
const的作用分析:
如果const位于号的左侧,那么const就是用来修饰指针所指向的变量的,即指针指向为常量;如果const位于号的右侧,那么const就是用来修饰指针本身,即指针本身为常量。
所以1和2两种情况是一样的,都是指针所指向的内容为常量,这两种情况下不能对内容进行更改。
int b = 500; const int* a = &b; *a = 600; //错误
要想使*a变成600,可以b = 600。或者使用下面的操作:
int b = 500, c = 600; const int* a = &b; a = &c; //此时a为600
并且这时候可以不进行初始化,因为虽然指针内容为常量,但指针本身不是常量:
const int* a; //正确
对于情况三,指针本身为常量,这时候不能对指针本身进行更改操作,但是指针所指向的内容不是常量。并且这种情况下,定义的同时必须进行初始化。
int b = 500, c = 600; int * const a ; //错误,没有进行初始化 int* const a = &b; *a = 600; //正确,指针指向不是常量,可以更改 *a++; //错误,指针本身不能更改
对于情况四,指针本身和指针指向都不能进行更改。
3.修饰类的成员变量
类的const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。如果类创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。如:
class a { const int size = 100; //错误 int array[size]; //错误,未知的size }
const数据成员的初始化只能在类的构造函数的初始化表中进行。
4.修饰类成员函数
有一些函数成员不改变类的数据成员,也就是说这些函数是只读函数,将这些函数加上const关键字进行标识,能提高程序的可读性。已定义成const的成员函数,假如想要修改数据成员的值,编译器会报错。同时应注意,关键字const必须以同样的方法出现在函数申明和实现中,不然编译器会将他们看成两个函数。但是c++也允许我们在数据成员的定义前面加上mutable,以允许该成员可以在常量函数中被修改。
在c++中,mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,甚至结构体变量或者类对象为const,其mutable成员也可以被修改。mutable在类中只能够修饰非静态数据成员。
int getnum() const ; //const成员函数
将const放在函数名的前面,那么意味着函数的返回值是常量,意义完全不一样了。
const成员函数能够访问对象的const成员,而其他成员函数不可以。const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。
5.修饰函数形参
const修饰函数参数的时候,表示函数的参数不能被改变。最常用的就是参数为引用时,为了增加效率同时防止修改。修饰引用参数时:
void function(const type& var); //引用参数在函数内为常量不可变
这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本, 然后传递过去,而引用直接传递地址,所以这种传递比按值传递更有效率。
6.修饰函数返回值
可以阻止用户修改返回值。返回值也要相应的付给一个常量或常指针。
三. explicit
c++中的explicit关键字只能用于修饰只有一个参数的类构造函数( 或者除了第一个参数以外的其他参数都有默认值), 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).
显示声明的构造函数和隐式声明的区别:
如果构造函数只有一个参数时, 那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象.
例如:x x = 10; 编译的时候自动转化为x x1(10); x x = x1;
explicit关键字的作用就是防止类构造函数的隐式自动转换.
explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit关键字也就无效了.
四.extern
修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。
1.extern修饰变量的声明。举例来说,如果a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。这里需要注意的是,a.c要引用到v,不只是取决于在a.c中声明extern int v,还取决于变量v本身是能够被引用到的。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。
2.extern修饰函数声明。从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程。
3.此外,extern修饰符可用于指示c或者c++函数的调用规范。比如在c++中调用c库函数,就需要在c++程序中用extern “c”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用c函数规范来链接。主要原因是c++和c程序编译完成后在目标代码中命名规则不同。