(一)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是C++中的一个关键字,用于在C++源码中内嵌汇编语言。
__asm关键字启动内联汇编并且能写在任何C++合法语句之处。它不能单独出现,必须接汇编指令、一组被大括号包含的指令或一对空括号。术语“__asm 块”在这里是任意一个指令或一组指令无论是否在括号内。
[3]
以下代码片段是在括号内的一个简单的__asm块。
_asm { mov al, 2 mov dx, 0xD007 out al, dx }
(三)typeid
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 可以用来指出,即使成员函数或者类变量为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构造函数只能被显式调用。