天天看点

C++中的关键字剖析(整理)

(一)volatile

volatile的作用是: 作为指令 关键字,确保本条指令不会因 编译器的优化而省略,且要求每次直接读值. 简单地说就是防止编译器对代码进行优化.比如如下程序:

1 2 3 4

XBYTE[2]=0x55;

XBYTE[2]=0x56;

XBYTE[2]=0x57;

XBYTE[2]=0x58;

对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入 volatile,则编译器会逐一的进行编译并产生相应的机器代码(产生四条代码). 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样, 编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在 寄存器里的备份。下面是volatile变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 这是区分C程序员和 嵌入式系统程序员的最基本的问题:嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所有这些都要求使用volatile变量。不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是真正懂得volatile完全的重要性。 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型参数, 编译器将产生类似下面的代码:

1 2 3 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;

}

(二)asm asm是C++中的一个关键字,用于在C++源码中内嵌汇编语言。

__asm关键字启动内联汇编并且能写在任何C++合法语句之处。它不能单独出现,必须接汇编指令、一组被大括号包含的指令或一对空括号。术语“__asm 块”在这里是任意一个指令或一组指令无论是否在括号内。 [3]  

C++中的关键字剖析(整理)

Visual Studio 2015起始页 以下代码片段是在括号内的一个简单的__asm块。 _asm { mov al, 2  mov dx, 0xD007 out al, dx } (三)typeid typeid用于返回指针或引用所指对象的实际类型

C++的typeid

编辑 注意:typeid是操作符,不是函数! 运行时获知变量类型名称,可以使用 typeid(变量).name(),需要注意不是所有编译器都输出"int"、"float"等之类的名称,对于这类的编译器可以这样使用:float f = 1.1f; if( typeid(f) == typeid(0.0f) ) …… 示例代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

#include <iostream>

#include <typeinfo>

using

namespace

std;

int

main(

void

)

{

// sample 1 

cout << 

typeid

(1.1f).name() << endl;  

// sample 2 

class

Base1 {}; 

class

Derive1:

public

Base1 {}; 

Derive1 d1; 

Base1& b1 = d1; 

cout << 

typeid

(b1).name() << endl; 

// 输出"class Base1",因为Derive1和Base1之间没有多态性 

// sample 3, 编译时需要加参数 /GR 

class

Base2 {  

virtual

void

fun( 

void

) {} 

}; 

class

Derive2:

public

Base2 { }; 

Derive2 d2; 

Base2& b2 = d2; 

cout << 

typeid

(b2).name() << endl; 

// 输出"class Derive2",因为Derive1和Base1之间有了多态性 

// sample 4 

class

Derive22:

public

Base2 { }; 

// 指针强制转化失败后可以比较指针是否为零,而引用却没办法,所以引用制转化失败后抛出异常 

Derive2* pb1 = 

dynamic_cast

<Derive2*>(&b2); 

cout << boolalpha << (0!=pb1) << endl; 

// 输出"true",因为b2本身确实是指向Derive2 

Derive22* pb2 = 

dynamic_cast

<Derive22*>(&b2);

cout << boolalpha << (0!=pb2) << endl; 

// 输出"false",因为b2本身不是指向Derive2 

try

{  

Derive2& rb1 = 

dynamic_cast

<Derive2&>(b2);  

cout << 

"true"

<< endl; 

}

catch

( bad_cast ) { 

cout << 

"false"

<< endl; } 

try

{  Derive22& rb2 = 

dynamic_cast

<Derive22&>(b2);  cout << 

"true"

<< endl; } 

catch

( ... ) 

// 应该是 bad_cast,但不知道为什么在VC++6.0中却不行?因为VC++6.0默认状态是禁用 RTTI 的,启用方式:project->setting->c/c++->category->c++ Language 下面第二个复选框选中。 

{  cout << 

"false"

<< endl; } 

return

0;}

(四)mutable mutable 可以用来指出,即使成员函数或者类变量为const,其某个成员也可以被修改。 在c++的类中, 如果一个成员函数被const 修饰,那么它将无法修改其成员变量的,但是如果这个成员变量是被mutable修饰的话,则可以修改。 (五)explicit explicit 构造函数的作用 解析: explicit构造函数是用来防止隐式转换的。请看下面的代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

class

Test1

{

public

:

Test1(

int

n)

{

num=n;

}

//普通构造函数

private

:

int

num;

};

class

Test2

{

public

:

explicit

Test2(

int

n)

{

num=n;

}

//explicit(显式)构造函数

private

:

int

num;

};

int

main()

{

Test1 t1=12;

//隐式调用其构造函数,成功

Test2 t2=12;

//编译错误,不能隐式调用其构造函数

Test2 t2(12);

//显式调用成功

return

0;

}

Test1的 构造函数带一个int型的参数,代码23行会隐式转换成调用Test1的这个构造函数。而Test2的构造函数被声明为explicit(显式),这表示不能通过隐式转换来调用这个构造函数,因此代码24行会出现编译错误。 普通构造函数能够被 隐式调用。而explicit构造函数只能被显式调用。