存档

‘C/C++’ 分类的存档

C++ Exception Handling

2010年6月9日 huubby 没有评论

最近刚刚读完《深度探索C++对象模型》,说来惭愧,整本书都快看完才想起来要做笔记。目前笔记只有后面三章的一些重点内容,本文是异常处理(Exception Handling)这部分内容的笔记整理。

写程序的时候,对于可能出现异常的地方,通常来说我们是这样写的:

try{
...
throw ...
} catch(...) {
//exception handling
}

当异常发生时,代码跳转到catch块,完成异常处理后退出。为了支持这个处理流程,编译器要能够在发生异常时,找到对应的catch子句。而查找catch子句时,编译器要知道函数的当前作用范围,同时还要了解抛出异常的类型,以便进行catch子句匹配(这引出了执行期类型识别的需求,即RTTI)。

详细来看看,上面这段代码大致可以分为三个部分:

throw子句,它抛出一个exception,可以是内置类型,也可以是用户自定义类型

catch子句,每个catch子句都是一个exception handler,表示其之后的代码段用来处理某种类型的exception

try段,内含一些程序代码,这些代码可能引发catch子句生效

当一个exception被抛出时,程序控制权从函数中释放,转而寻找一个匹配的catch子句。若不存在匹配的catch,默认的terminate()方法将被调用。而在控制权被放弃后,堆栈中的函数被poped up,函数内局部对象的析构函数在poped up之前会被调用。

当exception发生,编译系统完成下面几件必须要做的事:

1、检查发生throw exception操作的函数

2、决定throw是否是发生在一个try段内

3、如果throw发生在try段内,编译系统开始比较发生的exception的类型和catch子句中exception的类型是否吻合

4、一旦exception类型吻合,该catch子句就得到流程控制权

5、若throw并不是发生在try段内,或者没有找到吻合的catch子句,系统要做的就是,清理当前函数范围内所有局部对象,从堆栈中pop up当前函数,进入堆栈内下一个函数,继续步骤2-5

那么当一个exception被抛出后,它经历了些什么?它并没有无家可归,睡在天桥底下。抛出的exception object放在exception数据堆栈中,从throw处传递到catch子句的是这个exception object的地址。来看个例子:

catch (BaseException  newobj) {
    //do something
    throw ;
}

假设这个catch子句将收到一个DerivedException类型的exception object,DerivedException继承自BaseException。那么,由于exception类型吻合,按照上面说的步骤4,这个catch子句得到控制权。

catch子句中的newobj在收到exception时,将以抛出的exception object作为初值,像函数以传值方式传递参数一样。由于newobj是一个对象而不是指针或引用,BaseException的copy constructor将会被调用(如果存在的话),以拷贝原始exception object中属于BaseException的部分到当前的newobj中。

完成这些之后,catch子句内的代码被执行,再次抛出异常。这次抛出的是newobj还是原始的exception object?抛出的仍然是原始的exception object。newobj只是局部对象,在catch子句结束后被摧毁,任何对newobj的修改都被丢弃而不会影响到原始的exception object。这个原始的exception object将直到有一个catch子句执行完,并且不再抛出exception后,才会被摧毁。

本文内容来自《深度探索C++对象模型》第7章中的异常处理介绍。

关于C++的EH,在codeproject上有篇文章简单介绍编译系统与win32系统之间的协作关系,点这里

that’s all, over!

分类: C/C++ 标签:

error C2653: ’std’ : is not a class or namespace name

2010年4月28日 huubby 没有评论

Today, when I created a Win32 Console Application in VS2005, and wrote the code as below


#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
std::string    str = "";
str += "helllo";
str += "world";

printf("%s", str);
return 0;
}

I got compile errors:

error C2653: ’std’ : is not a class or namespace name

At the beginning, I thought it caused by a static library on which my new project depended. Then, I removed the static library dependent, but it didn’t work.

As you know, googling is a final solution when you find something you cann’t solve. I google it and figure it out. It’s simple, as MS has a bug in this situation.

You can find why this happend here, a relational situation about the issue is here.

Over!

分类: C/C++ 标签: ,

restrict关键字

2010年3月19日 huubby 没有评论

看代码看到个restrict,头一次知道还有这么个关键字,在维基查到基本用法和解释。本着好记性不如烂笔头的原则,翻译这篇维基贴在这里。

在C语言的一个标准(C99)里,有一个用于修饰指针声明的关键字-restrict,它是程序员给编译器的一个意向声明,表明只有此指针或者基于此指针的某个值(比如 pointer+1)可以访问其指向的对象。这个关键字限制了指针别名(译注:即指向同一对象的不同指针),目的在于进行编译器优化。如果这个程序员没有遵守这个意向声明,并且这个对象由一个独立指针访问到的话,会造成未定义的行为。( 译注:我也没搞懂这一大段话什么意思 ^_^ 不过看下面的例子应该差不多能理解这个关键字的用法)

如果知道只有一个指针指向某个内存块的话,编译器就能进行优化,以生成更高效的代码。下面这个例子能清楚解释这一点。

void updatePtrs(size_t *ptrA, size_t *ptrB, size_t *val)
{
    *ptrA += *val;
    *ptrB += *val;
}

这段代码里面,指针ptrA , ptrB, val可能指向同一块内存,所以编译器不能对代码进行优化。

load R1 ← *val  ; 加载val指针的
load R2 ← *ptrA ; 加载ptrA指针的值</pre>
add  R2 += R1   ; 相加
set  R2 → *ptrA ; 更新ptrA指针的值
; 对ptrB指针的操作类似,注意,这里val指针被加载两次,因为ptrA有可能和val指向同一块内存
load R1 ← *val
load R2 ← *ptrB
add  R2 += R1
set  R2 → *ptrB

如果使用restrict关键字,函数声明为:

void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val);

编译器就能知道ptrA, ptrB, val三个指针指向不同的内存块,更新其中一个不会影响到其他的,从而对代码进行优化。当然,这里要由程序员保证这三个指针不指向同一块内存。

load R1 ← *val
load R2 ← *ptrA
add  R2 += R1
set  R2 → *ptrA
;编译器知道指针val的值没有改变,所以没有重新加载它,优化了汇编代码
load R2 ← *ptrB
add  R2 += R1
set  R2 → *ptrB

还有一个例子是memcpy函数,memcpy(void*, void*, nbytes)的前两个指针参数被声明为restrict,告诉编译器两个数据块没有重叠,便于进行优化,使得memcpy的速度更快。如果程序员用相同的指针作为这两个参数的值,那么memcpy函数会产生未定义的行为。
(译注:memcpy的前两个参数确实声明为restrict,但是用相同的指针做参数传进去测试,没发现异常行为,此处存疑。)

另:推荐一个专栏,M. Tim Jones 专栏,Linux 内核、虚拟化及其他技术深层剖析。(你可以在这个专栏中看到restrict的身影)

分类: C/C++, Linux 标签: ,

socket流程

2010年3月18日 huubby 没有评论

失败,准备写的东西放狗一搜总是一大筐,这次不写了,直接贴网上找来的图。

TCP方式的流程:


UDP方式:


分类: C/C++ 标签: ,