C++不容易明白的关键字(一)

explicit

export 

mutable

volatile

using namespace

auto

asm register 



static_cast   dynamic_cast reinterpret_cast const_cast 

this inline

try catch throw

typedef
 
extern

sizeof typeid
 

static

1、explicit

        这个关键字对于好的代码是必要的。

        使用explicit关键字,将构造函数声明为显式构造函数,以防止隐式的类型转换

        explicit只对一个实参的构造函数有效。需要多个实参的构造函数不能用于执行隐式转换,所以无须指定为explicit。

        只能在类中声明的时候使用explicit关键字,在类外不能使用。

        除非有明显的理由需要定义隐式转换,否则,单参数构造函数应该声明为explicit,可以避免错误

        在C++标准库中,接受一个容量参数的vector构造函数,是explicit的。

2、export

        export关键字是一个很少被使用的关键字。它用于模板函数。在模板函数的定义时,加上export关键字,用于指明给定的定义可能会需要在其它文件中产生实例化,避免编译器为每个文件中的模板产生实例。

        在一个程序中,一个模板只能定义为export一次。

        一般是在函数模板的定义时使用。对于类模板,在类实现文件中使用。

        export关键字不是一个好的关键字,应当避免使用。在最新的C++11标准中,已经取消了该关键字。

3、mutable

        mutable是可变的意思,用于声明类的数据成员,称为可变数据成员。使用mutable修饰的类数据成员,即使在const成员函数内部,也可修改其值。

4、volatile

该单词的意思是易变的,不稳定的。它的作用是,作为指令关键字,确保本指令不会被编译器优化而省略,要求每次直接读值。比如:

  1. int x=10;  
  2. int a=x;  
  3. int b=x; 
  编译器在编译时发现,x的值在对a赋值语句和对b赋值语句之间未发生变化,由于在执行对a赋值语句的时候已经取出了x值,在对b赋值时,编译器直接将x值赋给b,而不是去寄存器或内存中再次读取,这是编译器的优化操作。

用途:

1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
试题:

1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:
1
2
3
4
int  square( volatile  int  *ptr)
{
     return  ((*ptr) * (*ptr));
}
下面是答案:
1). 是的。一个例子是只读的 状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的 指针时。
3). 这段代码是个恶作剧。这段代码的目的是用来返 指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数, 编译器将产生类似下面的代码:
4
5
6
7
int  square( volatile  int  *ptr)
{
     int  a,b;
     a = *ptr;
     b = *ptr;
     return  a*b;
}
由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
1
2
3
4
5
6
long  square( volatile  int *ptr)
{
     int  a;
     a = *ptr;
     return  a*a;
}
4
5
6
7
int  square( volatile  int  *ptr)
{
     int  a,b;
     a = *ptr;
     b = *ptr;
     return  a*b;
}
由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
1
2
3
4
5
6
long  square( volatile  int *ptr)
{
     int  a;
     a = *ptr;
     return  a*a;
}

典型的例子
1
for ( int  i=0; i<100000; i++);
这个语句用来测试空循环的速度的
但是 编译器肯定要把它优化掉,根本就不执行
如果你写成
1
for ( volatile  int  i=0; i<100000; i++);
它就会执行了


5、using namespace

所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。

namespace其作用是可以避免导致全局命名冲突问题。功能相似于Java中的import。

1namespace std编辑

namespace是指标识符的各种可见范围。命名空间用关键字namespace 来定义。命名空间是C++的一种机制,
用来把单个标识符下的大量有逻辑联系的程序实体组合到一起。此标识符作为此组群的名字。
C++ 标准程序库中的所有 标识符都被定义于一个名为std的namespace中。 由于namespace的概念,
使用C++标准程序库的任何 标识符时,可以有三种选择: [1]  
直接指定标识符
例如std::iostream而不是iostream。完整语句如下:
1
std::cout<<std::hex<<3.4<<std::endl;
使用using关键字
加入using std::cout; using std::endl; using std::cin; 则前面语句可以写成如下代码:
1
cout<<usingstd::hex<<3.4<<endl;
使用using namespace std
例如:
1
2
3
4
#include<iostream>
#include<sstream>
#include<string>
using  namespace  std

6、auto

C++ 98标准/C++03标准

同C语言的意思完全一样:auto被解释为一个自动存储变量的关键字,也就是申明一块临时的变量内存。

C++ 11标准

在C++11标准的语法中,auto被定义为自动推断变量的类型。例如:
1
auto  x=5.2; //这里的x被auto推断为double类型
1
2
3
4
5
6
map< int , int >m;
for ( auto  it=m.begin(); //这里it被auto推断为map<int,int>::iterator类型
it!=m.end();++it)
{
//....
}
不过C++11的auto关键字时有一个限定条件,那就是必须给申明的变量赋予一个初始值,否则编译器在编译阶段将会报错。

7、register 

 asm和register关键字一般很少使用。

      register关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,加快其存储速度,以提高效率注意是尽可能,不是绝对。一个CPU 的寄存器也就那么几个或几十个,你要是定义了很多很多register 变量,它不能全部把这些变量放入寄存器。

   使用register 修饰符的注意点

    但是使用register修饰符有几点限制。

  首先,register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度不过,有些机器的寄存器也能存放浮点数。

  其次,因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址

  由于寄存器的数量有限,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。

  在某些情况下,把变量保存在寄存器中反而会降低程序的运行速度。因为被占用的寄存器不能再用于其它目的;或者变量被使用的次数不够多,不足以装入和存储变量所带来的额外开销。


8、asm

__asm关键字启动内联汇编并且能写在任何c/c++合法语句之处.它不能单独出现.它必须接汇编指令、一组被大括号包含的指令或一对空括号.术语“__asm 块”在这里是任意一个指令或一组指令无论是否在括号内。
以下代码片段是在括号内的一个简单的__asm块。
__asm
{
   mov al, 2
   mov dx, 0xD007
   out al, dx
}
另一种方法是,你可以在每个汇编指令前放置__asm
__asm mov al, 2
__asm mov dx, 0xD007
__asm out al, dx
因为__asm关键字是一个语句分隔符,你也可以将汇编指令放在同一行:
__asm mov al, 2  __asm mov dx, 0xD007  __asm out al, dx
以上三个的例子产生相同的代码,但是第一种风格(把__asm块用括号括起来)有一些优势。括号可以清晰的将C或C++代码和汇编代码分开,并且避免了不必要的重复__asm关键字。括号也能避免模糊性。如果你想在__asm块的同一行放置一个C或C++语句,你必须将块用括号括起来。没有括号,编译器不能告诉汇编代码在哪里停止而C或C++代码在哪里开始。最后,因为在括号的文字有和原始MASM一样的格式,你能轻松的从一个已有的MASM源文件里剪切和黏贴文字到文件来。不同于C和C++的括号,包含__asm块的括号对变量的作用域并没有效果。你也能嵌套__asm块,嵌套对变量作用域也没有效果。

     

你可能感兴趣的:(C++不容易明白的关键字(一))