关于作者

用户名:hanyu1980
笔名:hanyu1980
地区:
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



访问统计:
文章个数:152
评论个数:34
留言条数:1




Powered by BlogDriver 2.1

韩羽的博客

 

设计改变中国!

文章

异常的Rethrow分析

网上看了一篇类似的分析,仔细看了一下写的不错,现在把代码引用一下:

try

       {

              try

              {

                     // 抛出一个异常对象

                     throw MyException("ex_obj1");

              }

              // 异常对象按值传递

              catch(MyException e)

              {

                     cout<<endl<<"捕获到一个MyException*类型的异常,名称为:"<<e.GetName()<<endl;

                     cout<<"下面重新抛出异常"<<endl<<endl;

                     // 异常对象重新被抛出

                     throw;

              }

       }

       // 异常对象再次按值传递

       catch(MyException e)

       {

    &nsp;         cout<<endl<<"捕获到一个MyException*类型的异常,名称为:"<<e.GetName()<<endl;

       }

这个地方异常作了四次构造,这四次构造分别称为obj1,obj2,obj3,obj4。逐一道来。

throw MyException("ex_obj1");看看这段代码。好像有一句话说,捕获的所有异常,永远不是异常对象本身。为什么这么说?MyException("ex_obj1")是构造了一个局部变量,但是这个局部变量被throw出去后会生成一个临时变量,我们所catch到的不是这个对象本身,而是这个临时对象。这个临时对象本身并没有什么,但却非常重要。因为rethrow是这个临时对象,而不是用值传递生成的新对象。所以用值传递的方式,不能将修改带到下一个catch块。后面两个catch就是obj3obj4了,以后就不用想什么,对obj3obj4所做的任何动作不会带到下一步,因为重新抛出的是obj2

其实异常抛出很简单的,用引用传递就可以了。使用引用传递的好处就是每次catch都可以做点小动作。呵呵!

- 作者: hanyu1980 2006年02月5日, 星期日 17:23  回复(1) |  引用(1) 加入博采

多人读单人写的MAP原理

今天,工作需要,领导教了一个多人读单人写的map的实现。

       首要的是需要一个事件,一个临界区和一个map

对于读操作需要Wait这个信号,如果成功了就可以读取map

对于写操作比较复杂,可以分为如下几步:

1.       Wait事件;

2.       进入加临界区锁;这里是为了保证只有一个线程访问这段。

3.       ResetEvent

4.       这里最好的办法是引用计数,目前在这里等了一会,等大家都出去。

5.       写操作。

6.       发信号。

7.       揭开临界区的锁

8.       离开。

今后我会做一个关于引用计数的实现。这并非本人所创,乃是今天学会的一个高招。

- 作者: hanyu1980 2006年01月19日, 星期四 21:47  回复(0) |  引用(1) 加入博采

已锁定
此日志的浏览权限已被作者锁定,请同作者联系,发送短消息,如果你的身份符合作者的要求,点击此处可以进行浏览

- 作者: hanyu1980 2006年01月19日, 星期四 10:18  回复(0) |  引用(1) 加入博采

静态成员函数不能调用普通成员变量

       这是个人人都知道的小问题,今天却折腾了心浮气躁的我20分钟。

       就这么一点错误。静态成员函数是类共享的函数,而普通成员变量则是类所独有的,所以在静态成员函数中调用普通成员变量,最起码直接调用是不可以的。

       这是一个小问题,但遇事要慢慢来,不要急。写这篇Blog最主要要提醒自己,凡事必有因,不要着急。

- 作者: hanyu1980 2006年01月18日, 星期三 18:15  回复(0) |  引用(1) 加入博采

Traits技法

接触traits的技法已近三年多了,对它的理解也是一个循序渐进的过程,所以现在总结一下,以为后来者鉴。

从迭代器的概念来说,迭代器和容器从逻辑上是分开的,容器存储,迭代器访问。OK,对于模板来说,它知道元素的类型;对于迭代器这真是个伤心的问题。其实它并不知道元素的类型,所以没有办法了,这个时候就开始约定。使用Traits来推算出类型来。

SGI的实现是比较典型。例如:

typedef typename iterator_traits<_Iterator>::value_type  value_type;

typedef typename iterator_traits<_Iterator>::reference reference;

typedef typename iterator_traits<_Iterator>::pointer   pointer;

下面是iterator_traits的实现,从中可以看出从迭代器中萃取出类型。

template<typename _Iterator>

    struct iterator_traits

    {

      typedef typename _Iterator::iterator_category iterator_category;

      typedef typename _Iterator::value_type        value_type;

      typedef typename _Iterator::difference_type   difference_type;

      typedef typename _Iterator::pointer           pointer;

      typedef typename _Iterator::reference         reference;

    };

vector中这样定义,

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >

    class vector : protected _Vector_base<_Tp, _Alloc>

{

typedef typename _Alloc::pointer                   pointer;

typedef __gnu_cxx::__normal_iterator<pointer, vector_type> iterator;

……

}

       这是Allocator中的定义:

template<typename _Tp>

    class allocator: public ___glibcxx_base_allocator<_Tp>

    {

   public:

      typedef size_t     size_type;

      typedef ptrdiff_t  difference_type;

      typedef _Tp*       pointer;

      typedef const _Tp* const_pointer;

      typedef _Tp&       reference;

      typedef const _Tp& const_reference;

      typedef _Tp        value_type;

……

}

这是问题的根源,迭代器归根结底通过这里得到元素的类型。萃取的起点在于Allocator,从这里开始,我们开始获取了元素的型别。

这篇写得很不充分,欲知详情,请参见SGI的实现。

- 作者: hanyu1980 2006年01月17日, 星期二 10:26  回复(0) |  引用(1) 加入博采

Effective STL之后记

终于开始写这篇后记了,从第一篇读后感的去年127号到现在,历时近40天。这40天时间在我看来虽然有点长,但是从效果来看,总的来说还是不错的。

       40天前,我对容器有一些经验,对迭代器经验也有,但是不是很足,其他环节就比较差了;现在各方面都有了提高。现在对各个库也有了一点体验,我想下部书的目标,应该更加底层一点。

- 作者: hanyu1980 2006年01月16日, 星期一 16:29  回复(1) |  引用(1) 加入博采

Effective STL之49-50

条款49:学习破解有关STL的编译器诊断信息

       STL的编译错误提示信息是非常让人烦恼的事情,读懂编译器的提示非常重要。STL中有很多typedef,所以一个原则就是替代法,在这个上面,UltraEdit能够提供一些帮助。

       其他还有一些总结在下面:

1.         对于vectorstring,迭代器有时是指针,所以如果你用迭代器犯了错误,编译器诊断信息可能会提及涉及指针类型。例如,如果你的源代码涉及vector<double>::iterator,编译器消息有时会提及double*指针。STLPort也许例外。

2.         提到back_insert_iteratorfront_insert_iteratorinsert_iterator的消息经常意味着你错误调用了back_inserterfront_inserterinserter,一一对应,(back_inserter返回back_insert_iterator类型的对象,front_inserter返回front_insert_iterator类型的对象,而inserter返回insert_iterator类型的对象。)如果你没有调用这些函数,你(直接或间接)调用的一些函数做了。

3.         类似地,如果你得到的一条消息提及binder1stbinder2nd,你或许错误地使用了bind1stbind2nd。(bind1st返回binder1st类型的对象,而bind2nd返回binder2nd类型的对象。)

4.         输出迭代器(例如ostream_iteratorostreambuf_iterators,和从back_inserterfront_inserterinserter返回的迭代器)在赋值操作符内部做输出或插入工作,所以如果你错误使用了这些迭代器类型之一,你很可能得到一条消息,抱怨在你从未听说过的一个赋值操作符里的某个东西。

5.         你得到一条源于STL算法实现内部的错误信息(即,源代码引发的错误在<algorithm>中),也许是你试图给那算法用的类型出错了。例如,你可能传了错误种类的迭代器。要看看这样的用法错误是怎样报告的,通过把这段代码喂给你的编译器来启发(并愉快!)自己.

6.         你使用常见的STL组件比如vectorstringfor_each算法,而编译器说不知道你在说什么,你也许没有#include一个需要的头文件。正如条款48的解释,这问题会降临在长期以来都可以顺利编译而刚移植到新平台的代码。

如上所说,替代法是个好方法,不过有些提示是在过于要命,只能自己忍受。

 

条款50:让你自己熟悉有关STL的网站

SGI STL网站,http://www.sgi.com/tech/stl/

STLport网站,http://www.stlport.org/

Boost网站,http://www.boost.org/或者http://boost.sourceforge.net/

记住它们的地址,经常访问它们。

- 作者: hanyu1980 2006年01月16日, 星期一 13:56  回复(0) |  引用(1) 加入博采

Effective STL之47-48

条款47:避免产生只写代码

       这里的只写代码和文件的只写属性并不完全是一回事,意思是写的代码为了让别人去读,写出天书并不是一件很光荣的事情。我是这样觉得,特别是觉得很多连写,会觉得效率会提高的看法是可笑的。代码的长度和效率不是一个概念。STL本来就难以被理解,作为开发者有义务让不熟悉STL的人读懂代码。

      

条款48:总是#include适当的头文件

不需要太多内容,这就是全部:

l         几乎所有的容器都在同名的头文件里,比如,vector<vector>中声明,list<list>中声明等。例外的是<set><map><set>声明了setmultiset<map>声明了mapmultimap

l         除了四个算法外,所有的算法都在<algorithm>中声明。例外的是accumulate(参见条款37)、inner_productadjacent_differencepartial_sum。这些算法在<numeric>中声明。

l         特殊的迭代器,包括istream_iteratorsistreambuf_iterators(参见条款29),在<iterator>中声明。

l         标准仿函数(比如less<T>)和仿函数适配器(比如not1bind2nd)在<functional>中声明。

- 作者: hanyu1980 2006年01月16日, 星期一 13:14  回复(0) |  引用(1) 加入博采

Effective STL之45-46

条款45:注意countfindbinary_searchlower_boundupper_boundequal_range的区别

       对无序区间的查找,我们当然是find,只有当需要统计拷贝的个数,count才有意义。如果对有序区间binary_search告诉你是否有。如果要知道位置,equal_range是一个好的选择。对有序需要的统计当然distance配合equal_range当然要比count的效率高。

1.       期望值是否存在?

(1) 无序区间:find

(2) 有序区间:binary_search

(3) set或者mapcount

(4) multiset或者multimapfind

2.       期望值是否存在?如果有,第一个等于这个值的对象在哪里?

(1) 无序区间:find

(2) 有序区间:equal_range

(3) set或者mapcount

(4) multiset或者multimapfind或者lower_bound

3.       第一个不在期望值之前的对象在哪里?

(1) 无序区间:find_if

(2) 有序区间:lower_bound

(3) set或者maplower_bound

(4) multiset或者multimaplower_bound

4.       第一个在期望值之后的对象在哪里?

(1) 无序区间:find_if

(2) 有序区间:upper_bound

(3) set或者mapupper_bound

(4) multiset或者multimapupper_bound

5.       有多少对象等于期望值?

(1) 无序区间:count

(2) 有序区间:equal_range,然后distance

(3) set或者mapcount

(4) multiset或者multimapcount

6.       等于期望值的所有对象在哪里?

(1) 无序区间:find(迭代)

(2) 有序区间:equal_range

(3) set或者mapequal_range

(4) multiset或者multimapequal_range

       当然,序列容器要用泛型算法,而关联容器用成员函数。

 

条款46:考虑使用函数对象代替函数作算法的参数

作为参数,如果比对器,自己的设计和STL的设计有什么区别?

如果是纯函数,一般来说,算是一次函数调用,而一般STL的调用operator()是内联函数,这样效率会高一些。所以要善用内联对象可能效率会更好一些。这和39有点冲突,需要博弈。

- 作者: hanyu1980 2006年01月16日, 星期一 10:34  回复(0) |  引用(1) 加入博采

Effective STL之43-44

条款43:尽量用算法调用代替手写循环

       Meyers阐述的优点:

1.效率:算法通常比程序员产生的循环更高效。

2.正确性:写循环时比调用算法更容易产生错误。

3.可维护性:算法通常使代码比相应的显式循环更干净、更直观。

手写循环,可能直接导致迭代器实效。这真是一个让人发疯的问题。可以通过一些仿函式来避免一些此类错误。所以这类问题有一个权衡。如果手写循环简单清晰,而且是原有STL没有提供的功能,否则还是选择STL的实现吧。

关于accumulate对副作用敏感,而for_each不敏感的区别在前面提到,如果有兴趣可以阅读代码。For_eachtransform大不相同,这里也不赘述了。

 

条款44:尽量用成员函数代替同名的算法

       这一条我知道的很早,最早用STL就知道这一原则。这里不用多说什么。两点:1.各种容器的成员函数有关于自身的优化。泛型只是最简单的线性操作。2.记得list吧,他的很多成员和泛型算法名字相同,其实大不相同。

- 作者: hanyu1980 2006年01月13日, 星期五 09:33  回复(0) |  引用(1) 加入博采