九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

计算机业最振奋也是最残酷的特性就是喜新厌旧

今天从各类网站,辗转跳转,从《梦断代码》跟到《新机器的灵魂》,突然在

《新机器的灵魂》IT业魂在何方?

 

文中,看到了一句话," 计算机业最振奋也是最残酷的特性就是喜新厌旧。",很感叹。。。。呵呵,大家自由想象。。。。。。。。。起码从编程语言上来想。。。此话实在不假,虽然C也坚持了这么多年了。

各类网站,新特性等等,喜新厌旧,无孔不入,不过,就是这个新。。。不也是代表着创新及激情吗?。。一句话,也可以引发人无限的思考。

 

"

但疯狂的节奏下,一些内在的东西是弥久的,甚至是永恒的。毕竟任一行业,其真正的主角都是人,而人恐怕是世界上最难升级的事物。写于22年前的《新机器的灵魂》无疑是一本经得起时间锤打的经典。它讲述了一群忘我工作的电脑技术人员,制造一台新型计算机的动人过程。可以说,本书定义了计算机业整整一个时代的灵魂和精神。尽管业内技术2年一变,人物5年一换,但这个产业的问题和驱动力与10年、20年前相比并无两样,只是不同的公司扮演着不同的角色而已。无论是后来的PC之火,还是互联网浪潮,都没有脱离贯穿这个产业数十年的本质。

"

 

有时看到一些文字真的很感叹。。。。为啥人家水平那么高呢?。。。。。。呵呵,学习了。

阅读全文....

纯YY一下,在The Python Tutorial中,从Guido van Rossum的例子中可以看出他对现在流行的脚本语言perl,tcl,ruby,lua,python的评分

In addition to alternative list implementations, the library also offers other tools such as the bisect module with functions for manipulating sorted lists:

>>> import bisect
>>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')]
>>> bisect.insort(scores, (300, 'ruby'))
>>> scores
[(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]
摘自:Python 2.6.1的文档,Brief Tour of the Standard Library – Part II章,Tools for Working with Lists节
呵呵,很明显可以看出Guido van Rossum的意图,以scores来表示含义,然后分别给了perl 100分,tcl 200分, ruby 300分, lua 400分,然后给了其创造的语言python 最高的500分:)
其他的都还能理解。。。。。不过ruby在其心中排在lua之后,实在有点让ruby阵营的人抓狂-_-!呵呵,虽然个人没有学过ruby,仅学过lua。。。。。。
呵呵,纯YY一下,这是偶然从文档中查资料的时候翻出来的:)

阅读全文....

程序员平时都是木讷的,但是谈到计算机或者程序的时候简直就是天才—兼借题发挥,谈谈语言及工具的选择

程序员平时都是木讷的,但是谈到计算机或者程序的时候简直就是天才兼借题发挥,谈谈语言及工具的选择

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

程序员们对各类事物的敏感度可能都不够,但是对于计算机和程序语言的敏感度实在太高,所以,在这个他们赖以为生的领域,他们的聪明才智发挥的是若此的淋漓尽致。

最近看到CSDN上的

语录:101条伟大的计算机编程名言》,觉得很有意思:)

其中有很多,是真的属于程序员才能理解的幽默:

最能让人会心一笑的是61条:

61、我想微软之所以把它叫做.Net,是因为这样它就不会在Unix的目录里显示出来了.(Oktal)

不理解的兄弟。。。。和你解释了也就没有效果了。

 

也有很多的确是属于经验之谈:

32、好的软件的作用是让复杂的东西看起来简单.(Grady Booch,UML创始人之一)

62There is no programming languageno matter how structuredthat will prevent programmers from making bad programs.(Larry Flon)

(这是唯一文章中没有翻译的,不知道为啥漏了,意思是无论结构多好的程序语言都不能阻止程序员写出垃圾程序。)

75、好代码本身就是最好的文档.(Steve McConnell)

 

另外,因为国外对s e x这种事情看的比较普通,所以例子中不乏相关的类比:

70、说Java好就好在运行于多个操作系统之上,就好像说肛 交好就好在不管男女都行.(Alanna)

73、软件就像性事:免费/自由更好.(Linus Torvalds)

 

程序语言是程序员的信仰,其圣战从未停止过:

57、只有两种编程语言:一种是天天挨骂的,另一种是没人用的.(Bjarne Stroustrup,C++之父)

58PHP是不合格的业余爱好者创建的,他们犯做了个小恶;Perl是娴熟而堕落的专家创建的,他们犯了阴险狡诈的大恶.(Jon Ribbens)

 

微软作为世界软件世界的实际霸主,相关的也不少,除了.net那一条,我觉得还比较有意思的是:

18、微软有出了个新版本,Windows XP,据大家说是'有史以来最稳定的Windows', 对我而言, 这就好像是在说芦笋是'有史以来发音最清脆的蔬菜一样' (Dave Barry)

 

调试永远是占用着程序员绝大部分时间:。。。。。

14、我终于明白'向上兼容性'是怎么回事了.这是指我们得保留所有原有错误.(Dennie van Tassel)

84、如果调试是除虫的过程,那么编程就一定是把臭虫放进来的过程.(Edsger W. Dijkstra)

 

为了增加主题的契合度,我还引用一些国内有意思的例子,某年某月,当大牛开始贬低C++的时候,国内程序员突然牵扯到C++,Python上的争吵:

d 发表于2007911 11:59:29  IP:举报

C++还在讨论String的时候.Python已经做好了项目在喝酒了.我也有一句.python在为开发一个操作系统无能为力的时候.C++已经在喝COFI,.

gussing 发表于2007911 12:21:01  IP:举报

是啊,可以喝咖啡,多爽
python
程序员喝了十年咖啡,c程序员熬了十年夜,后来在一次聚会上他们相遇了,python还在喝咖啡,c程序员则买了一艘豪华游艇准备出海旅游。python 程序员一脸兴奋的说:老兄,知道吗,python又出新库了!c程序员也一脸兴奋,说:当然知道,那正是我带领的一个团队开发的。

irplay 发表于2007911 12:45:43  IP:举报

python什么垃圾啊...高不成低不就的语言都要淘汰.

hehe 发表于2007911 13:20:52  IP:举报

gussing 发表于2007-09-11 12:21:00 IP: 220.248.25.*
是啊,可以喝咖啡,多爽

python
程序员喝了十年咖啡,c程序员熬了十年夜,后来在一次聚会上他们相遇了,python还在喝咖啡,c程序员则买了一艘豪华游艇准备出海旅游。python 程序员一脸兴奋的说:老兄,知道吗,python又出新库了!c程序员也一脸兴奋,说:当然知道,那正是我带领的一个团队开发的。
--------------------------------------------
一百个python程序员都在喝咖啡,一百个c程序员中只有一个买了游艇,剩下99个还在加班

笑死 发表于20071228 1:32:01  IP:举报

刚喝完咖啡,忽然客户过来说:"太慢了,我去找人重新开发.我们的合作到此为止啦".

 

 

呵呵,C++程序员对于Python运行速度的攻击,Python程序员对于C++开发效率的攻击都是如此的尖锐并且有思想:)很有意思,我印象深刻,好不容易翻出来啊。。。。。

 

说到圣战:。。。VIMEmacs的圣战被誉为从计算机发明那天就开始,计算机历史上持续最长时间的圣战:)有很多相关资料,这里是一个

EMACS 对 vi:永无止境的圣战

。。。无奈的是。。。我作为靠C++谋生,业余对Python很感兴趣,并且无论在Linux,Windows下都使用着VIM的家伙,几大圣战的讨论我都是被攻击的当事人-_-!(因为攻击者肯定都是全面积覆盖)

其实。。。从使用者的角度来说,没有东西是完美的,但是都存在选择,当年也在EMACSVI中选择,最后我发现我要的仅仅是一个在任何时候都可以使用的方便的文本编辑工具的时候,我选择了VIM,虽然也有被那句很经典的话打动的意思:EMACS的程序员都希望增加一个脚踏板。。。呵呵

当我觉得我需要是一种通用的编程语言,但是却要足够的接近底层的时候,我选择了C++,这个领域我没有其他选择,当我开始网络游戏服务器/客户端开发的时候,我需要一种高效的嵌入式脚本语言(工作需要)我选择了LUA这样的小众语言,再后来,我感觉自己希望加深对Linux/Unix的理解,并且习惯在Shell下工作时,我学习了bash(不知道是用的太少,还是没有理解其精髓,总感觉语法过于扭曲,实际上用bash的时候更喜欢用其兼容csh”(())”语法,特别是当我不当使用,用其实现算法的时候-_-!),再再后来,我希望有一种足够好,足够通用,并且和C/C++能够很好交互的脚本语言时,我选择了Python,它也一直在给我惊喜:)虽然它的速度实在不咋的,但是,当我开始使用Python的时候,速度明显不是我关心的东西了,别忘了。。。作为程序员,我的母语是C++。理解你需要的,做出适当的选择,可能这才是王道。。。。。

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

 

阅读全文....

其实C++比Python更需要lambda语法,可惜没有。。。。

其实C++比Python更需要lambda语法,可惜没有。。。。

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

思路有点混乱,想到哪就写到哪了。

起点是PythonPython的语法大都简单,有人说接近自然语言,但是其实我还是发现了一些不那么通俗易懂的东西,也许是因为还没有形成深刻的Python思维,也许是受C++的熏陶太深吧,其中,Python的函数式编程方式上就有很多地方感觉语法比较不那么容易一眼就看出来。其实想到函数式编程,倒是首先想到了lambda,自此思路混乱。对于将函数作为第一类(first class)值的语言,比如Python,lua中,感觉lambda的作用就不是那么明显,比如Python中,调用lambda的实际需求似乎感觉不算太大,再加上其函数定义的语法本身就足够的简洁,定义一个完整的函数实在也不算什么。作为语法糖的lambda,实在就显得有点甜味不足,虽然也不是说完全没有用。

这里举几个例子:

def add1(a,b):  return a + b

add2 = lambda a,b : a + b

print add1(1,2)
print add2(1,2)

fun = lambda f,a,b : f(a,b)

print fun(lambda a,b : a * b, 3, 4)

print fun(add1, 3, 4)

 

可以看出,对于lambda这样的语句,假如在Python中还有作用的话,那么就是在需要函数作为参数传递的时候了,但是个人还是保持这样的观点,因为python这样的动态类型语言+足够简洁的函数定义语法+函数第一类型值的特点导致实际上lambda不够甜,虽然其可以进一步的简化一些语法。

但是,假如C++中有lambda那就是完全不同的概念了,看看同样的事情吧:

int add(int a, int b)

{

    return a + b;

}

 

template<typename T>

T add1(T lhs, T rhs)

{

    return lhs + rhs;

}

 

template<typename T>

class add2

{

public:

    T operator()(T lhs, T rhs)

    {

       return lhs + rhs;

    }

};

 

template<typename FUN, typename T>

T fun(FUN function, T lhs, T rhs)

{

    return function(lhs, rhs);

}

 

 

int main()

{

    int a = 1;

    int b = 2;

 

    cout << add1(a,b) <<endl;

    cout << fun( ptr_fun(add), 1, 2) <<endl;

    cout << fun( add, 1, 2) <<endl;

    cout << fun( add1<int>, 1, 2) <<endl;

    cout << fun( ptr_fun(add1<int>), 1, 2) <<endl;

    cout << fun(add2<int>(), 1, 2) <<endl;

 

    system("PAUSE");

}

 

可以看到,相对而言C++的函数定义语法,包括使用方式上都会比Python要复杂一些,最主要的复杂性来之于C++赖以生存的根本,强类型,为了达到类型安全又类型无关,引入的模板机制,进一步的复杂化了函数的定义。

最有意思的是,

    cout << fun( add1<int>, 1, 2) <<endl;

    cout << fun( ptr_fun(add1<int>), 1, 2) <<endl;

 

这两种方式,在开启/clr,即开启微软的公共运行时库时会出现异常,我还是在反汇编时发现汇编代码异常才发现原来我在上次调试for_each的时候,为了展示/clr的新添的for each语法而开启了/clr选项,详情可以见《多想追求简洁的极致,但是无奈的学习C++for_each的应用》一文。

/clr选项应该就是将C++加入微软的.net体系,因为没有深入学习过,所以对出现的异常现象无法解释,希望有学过.net的人可以解释一下。

 

另外,这里想说的是,因为C++没有原生的for each语法,所以实际上很多循环不想写的话只能通过for_each算法来模拟,再加上很大一族的算法函数对于函数对象的需求,导致了语法的极大复杂化,使得C++对于lambda语法的需求远远大于Python这样的语言,但是,遗憾的是,C++并没有这样的语法,实际上09标准中好像有人提议过这样的特性,不过不知道通过没有-_-!就我目前所知,否决的可能性要大得多,但是因为没有仔细去查阅资料了,所以不敢肯定,但是很明显TR1中是没的,新出来的g++中新添了很多09标准要出来的特性中也没有包含lambda,似乎我们可能在短时间内(起码下一版标准。。。又要起码十年)是没有机会了。

多想追求简洁的极致,但是无奈的学习C++for_each的应用》一文,为了能够简单的让c++模拟出for each语法的调用,我甚至都不得不将很多循环进行两次。。。。。。详细情况见前文,就知道假如能有lambda,那么我们就能够省下多少函数的定义了。这里我就不在重复原文中的例子了。

事实上,还是如同boost::foreach库一样,虽然没有这样的语法,但是C++界的牛人们绝不允许这样的情况发生,他们想要什么,就会有什么,如同当年上帝。。。。要有光,于是就这么有了。。。。。看看boostlambda库能给我们带来什么。

#include <list>

#include <iostream>

#include <boost/foreach.hpp>

#include <boost/lambda/lambda.hpp>

using namespace std;

using namespace boost::lambda;

 

template<typename T>

void printInt(T i)

{

    cout <<i <<endl;

}

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    for(list<int>::const_iterator lit = l.begin(); lit != l.end(); ++lit)

    {

       cout <<*lit <<endl;

    }

 

    // 同样是需要输出

    for_each(l.begin(), l.end(), ptr_fun(printInt<int>));

 

    BOOST_FOREACH( int i, l)

    {

       cout <<i <<endl;

    }

 

    // 因为Andrew koenig发明的操纵器的特性,boost::lambda无法支持

    for_each(l.begin(), l.end(), cout <<_1 <<'/n');

 

    system("PAUSE");

}

 

一条语句,省下了很多工作,只当有这样简洁的实现方式时,我们才能够大规模的利用for_each去代替无聊的循环,非常高频率出现的循环,并且,虽然说Boost实现的lambda语法和一般而言有点区别,但是还算是比较容易理解,在没有for each语法前,我最希望有的可能就是lambda语法了,我希望利用算法来代替循环,但是没有lambda前,那样做可能比不费脑子的写个循环更加费力不讨好,甚至流于语言特性的滥用。还是在多想追求简洁的极致,但是无奈的学习C++for_each的应用》一文中,后面的例子就可以看出在没有这些语法特性时,我希望实现一个那么简单的功能所付出的代价。

 

这里为了方便,还是拷贝一次原文中的例子:

Python中:

def add(a,b):
    
return a + b

l = [1,2,3,4,5]
for i in l:
    
print add(i,1)

 

无非就是在每个输出的函数中调用一个函数,没有任何值的一提的地方,是个人就能看懂。

C++需要实现成下面这个样子:

#include <list>

#include <iostream>

#include <algorithm>

#include <functional>

using namespace std;

 

template <typename T>

class Add : public binary_function<T, T, void>

{

public:

    void operator()(const T& ai, const T& aj) const

    {

       cout <<(ai + aj) <<endl;

    }

 

};

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    for_each(l.begin(), l.end(), bind2nd(Add<int>(), 1));

 

    system("PAUSE");

}

 

这是在没有lambdafor each语法时无奈的实现。

假如有lambda语法的话,那么感觉就不同了。

#include <list>

#include <iostream>

#include <boost/foreach.hpp>

#include <boost/lambda/lambda.hpp>

using namespace std;

using namespace boost::lambda;

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    for(list<int>::const_iterator lit = l.begin(); lit != l.end(); ++lit)

    {

       cout <<*lit + 1 <<endl;

    }

 

    for_each(l.begin(), l.end(), cout <<_1+1 <<'/n');

 

    system("PAUSE");

}

 

还是几乎与原有的不+1输出实现同样的简洁,这就是lambda带来的特性,在没有这些特性的时候,我们就只能老老实实的按iterator的方式写了,在C++苦海中挣扎的兄弟们啊。。。。(特别是像我这样,工作中甚至连boost都不能用的人啊。。。。)继续等待吧。。。。等09标准出来。。。等VS202X版本的VS出来后,估计差不多才可能实现新的C++ 09标准,然后我们也许能够稍微减轻点工作量,以后能够写成大概是

    for(auto lit = l.begin(); lit != l.end(); ++lit)

    {

       cout <<*lit + 1 <<endl;

    }

这个样子。。。。。。。。。。。。。。阿门。

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

程序员手中的利器(1)--工欲善其事必先利其器

程序员手中的利器(1--工欲善其事必先利其器

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

不要和讲述工具不是最重要的理论,我知道,并且就是一直被其所害。这里我得说明一下,在最开始学习某种编程语言的时候的确应该主要关注与语言特性的学习而不是被一些复杂的IDE所分心,但是当真的成为一个程序员后,当渐渐的一种语言对你来说也就是一种工具的时候,那些IDE一样的工具,也应该值得你花同样的实现去学习!

这里主要也就是列出平时我用到的工具,自从刚开始工作的时候被总监一句“工欲善其事必先利其器”的话警醒,从以前学习时那种语言才是最重要的,IDE什么都仅仅是个工具而已的思想中走出来。我开始有意识的花功夫去学习那些以前我很不屑去学习的工具。事实上,作为一个计算机面前工作的人员(其实不仅仅是程序员),熟悉好你手中的每一个工具,那都是节省你时间的法宝,同时,节省的是你的生命。(虽然说主要是提高你的工作效率。)并且,因为好的工具可能最主要关注的是熟练使用者的使用效率,所以往往都不那么“初学者友好”,使很多人望而却步,但是在我逐渐的去熟悉了这样一些工具以后,我总是会发现,我用来学习一个好工具的时间,到头来总是会远远的少于用一个好工具为我节省的时候,所以,一个好工具几乎总是值得投入足够的精力去学习的,毕竟,学会了一个好工具会一直受用!这点,我深有体会!

 

作为一个列表,得有个分类和顺序,先列表,然后再讲理由吧。

文本编辑工具:特别推荐vim! Windows/Linux/X多平台通用

辅助:Ultra Edit:Windows下偶尔用用

IDE: Visual Studio 2005(VS2005)  Windows下的C++开发环境

Eclipse: Windows/X 多种语言跨平台综合开发环境

代码控制:Visual Source Safe(VSS)Windos下与VS2005结合使用

SVN,CVSLinux下及开源需要

调试:gdb linux/X 需要

pydb: Linux Python语句调试

bashdb: Linux bash 语言调试

工程控制:GNU makeLinux

编译器: gccLinux

UML IBM Rational rosewindows,最强大的UML工具,毋庸置疑

测试工具:Compuware DevPartner windows下用于检测代码错误或者内存泄露

备选工具:Visual Studio Team版自带C++代码分析工具

Profiler: AQTimeWindows下代码运行时间分析工具

8.    资源管理器:Total CommandWindows

9.    比较工具:Beyond compareWindows

10. 16进制编辑工具:WinHex, 我认为Windows下最佳的16进制编辑工具

HViewWindows,同样优秀,除了界面有点老,支持对PE文件格式的分析及内带反汇编功能

11. 反汇编静态分析:IDA ProWindows/Linux,基本上,有这一个就够了,w32dm什么的我好像没有需要过。

12. 反汇编动态调试工具:OllyDbg, Windows 的确比较容易上手,功能也很强大

       SoftIce,Windows ring0调试工具

13. SSH客户端: WinSCP,图形界面,主要用于远程登陆Linux服务器做文件管理用

       Putty 个人感觉不错的远程控制台

以上Linux都是特指Linux下的控制台,X才是指Linux下的图形界面环境

 

从此列表中可以看出,我的主要工作环境还是Windows,这是工作需要,但是由于工作中服务器需要跑在Linux下加上个人爱好,所以也掌握了一些Linux下的工具,但是由于我主要是通过Putty登陆Linux服务器工作,所以对于X下的工具了解不多。再加上个人爱好和前段时间反外挂的工作,所以还了解了一些反汇编工具的使用。

再说一次的是,这个列表仅仅是我个人平时使用的软件列表,并不代表人人都喜欢,或者世界最高水平。。。。。。-_-!

 

以下为逐条分析,因为精力有限,可能主要讲一些我个人的看法,至于大家都知道的东西讲太多也没有意义,并且以精简为主,不然的话光是讲vim好用的插件都可以用一天的时间来写。另外。。。本来准备写一篇小文章来总结一下自己所用工具的,但是发现这个话题一旦开始,就很难收尾了。。。。结果。。。又是一个系列。

BTW:万恶的ASP.net,明显和FireFox不兼容,我在FF中发表的文章在IE中都看不到,虽然可以参考原则,IE不能看的空文章用FF浏览。。。但是,这些文字也看不到吧。。。还是重发一下吧 

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

小评《卓有成效的程序员》—《The Productive Programmer》

这不是写给初学编程的人看的书,他们还是好好的听好亘古不变的教诲,“努力学习语言本身,不要被工具所干扰”;这不是写给一般的程序员看的书,他们迷失在各种各样的新兴语言之中;这也不是给一个对编程这种工作没有热情的人看的,因为工作效率再高,对他们来说,也仅仅是被剥削的更加严重。但是,假如你是和我一样,热爱着编程,即便是被剥削至最后一滴血也绝不罢休的提高着自己的工作效率,能够用键盘完成的绝不用鼠标完成,能够点一次鼠标完成就绝不点第二次,能够敲一个键完成的绝不敲第二个键,能够自动化完成的绝不手动完成的程序员的话,这会是你想要看的书,也会是你喜欢看的书,你会发现,原来你还有志同道合者,原来还有人和你一样,懂得怎么艺术性的操作电脑,懂得怎么让电脑干它该干的事情,而不是让自己去干那些重复性的工作,哪怕老板交代的任务多么枯燥和具有重复性,也能有技术的艺术性的完成,而且,比一般人完成还要快一个数量级。

当我看到书的前言部分,我就知道这是我想要看的书,几乎是一口气读完,书不厚,但是沉淀的都是实打实的技术,不是什么奇技Y巧,都是提高工作效率的有用途径,虽然这样说似乎有枪手的嫌疑了-_-!所以,这里还是补充一句。。。。。出版社是我不喜欢的机 工,奇怪的是为什么那么多好书,特别是O’REILLY的书都被他们弄走版 权了,看看那些无奈的蝌蚪文所谓影印版吧,看看那些经典书籍被印在什么样透明的纸上了。。。。。。对于出版社,个人还是更喜欢人民邮电(真的是有责任性的出版社)和清华,电子工业还说的过去。晕,我这个人就是常常不靠谱,说了这么多题外话,但是总的来说,这本书的印刷质量在机工中应该还算中上,不至于像《Windows核心编程》那么恶心。(指的是印刷质量,书绝对是好书)

书中第一部分的机制给出了很多有意思的法则,也提供了很多在不同平台下的实际有用工具,很多东西会让你很有收获,加速器,宏,虚拟桌面等,的确是是实实在在的有用,第二部分相对第一部分来说更加与语言接近了,由于本人的母语是C++,完全没有学过JAVA,Groovy等语言,相对而言很多语言的示例对我来说就没有那么实用及启发性,但是其思想还是在的,虽然个人感觉要在我们公司这样的项目中实行测试驱动开发(TDD)好像不太可能。

总体而言,书中对于命令行的推崇较合我意,对于编辑器的各种评价,包括对本人最爱的VI的很高的评价,包括对正则表达式的评价及使用都和本人自己在工作中的使用感受非常贴近。有的时候,你不知道一件事情,或者你觉得一件事情比较难学,然后就放弃了,其实,放弃的是一片天空。。。。。。我无数次的享受着VI及正则表达式带给我的便利,但是也许我没有办法将其描述的像作者那样吸引人,:)

《小小游戏程序员四个月工作总结》中后面一部分也是我曾经使用过的例子。

另外,这里想说的额外一点就是,正则表达式的使用远远不仅仅是匹配某个数据而已,当你想在一大堆代码中某个特定位置添加某条或某段语句的时候,善用替换的方式,可以节省你大量的时间,这几乎是我解决类似问题的必然途径(作为小程序员,老是会为了减轻总监等的负担而接受这样的枯燥任务),但是,个人感觉,在乏味的工作,都能有艺术性的解决方式。

当然,你按照书中所讲的方式去做,肯定能提高自己的工作效率,但是,首先这是一种思想,起码你要有对提高自己工作效率一种追求,不然,讲的再多也是白费。几乎可以肯定的是,最大的提高工作效率的方式绝对来自你自己工作中的需求,而不是其他人的方式。

我们可以以Larry Wall说的”懒惰、傲慢、缺乏耐性是程序员的三大美德”为准绳,来提高效率。

这看起来似乎不可理喻,但听听解释你就会同意这个说法了。

懒惰:因为好的程序员会致力于减少需要完成的工作量,想尽一切办法减少。

傲慢:容易被荣誉感冲昏头脑,所以会把程序写的尽可能的完美,免得被别人嘲笑。

缺乏耐性:坚决不做重复性的工作,那是计算机应该做的。遇到重复性工作就想尽一切办法来交给计算机做。尽管有时候这可能会花比直接重复一下更长的时间。

我阅读的版本:
卓有成效的程序员

原创文章作者保留版权 转载请注明原作者 并给出链接

九天雁翎(JTianLing) — www.jtianling.com

阅读全文....

多想追求简洁的极致,但是无奈的学习C++中for_each的应用


多想追求简洁的极致,但是无奈的学习C++for_each的应用

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

for each语法是方便的,也是很自然的,这也是为什么很多语言都有这样的语法,就我所知,包括java(jdk5.0以上),python,php,asp.net等语言都有类似的语法,甚至微软为C++/CLI中也添加了这样的语法。但是很遗憾的是,C++98标准中没有,于是,我们只能通过可悲的for_each算法去模拟。。。。。。。。。。先看看原生的语法是多么方便和自然的吧,虽然有人将其视为语法糖,但是,就算是糖,这也是很甜的那种。

先看看Python中的循环,虽然不是for each,但是类似于。

l = [1,2,3,4,5]

for i in l:

print i

简洁,干净,

假如你有幸使用微软的托管C++,你可以使用类似的语法:

using namespace System;

 

#include <list>

#include <iostream>

using namespace std;

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    for each ( int i in l)

       Console::Write(i);

 

 

    system("PAUSE");

}

虽然作为强类型语言,在声明方面稍微复杂点,循环的处理还是那么简洁,干净。

 

再来看看现有的C++中的:

#include <list>

#include <iostream>

using namespace std;

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    // 同样是需要输出

    for(list<int>::const_iterator lit = l.begin(); lit != l.end(); ++lit)

    {

       cout <<*lit <<endl;

    }

 

    system("PAUSE");

}

繁复到我都不想说了,list<int>::const_iterator似的迭代器声明语法不符合一处定义的原则,冗余信息太多。(C++09添加的auto用法就是解决此问题的),即便是解决了此问题,还是会发现,在C++中写个循环比在python(仅仅是一个例子,其他有类似for each特性的语言都比C++简单)中复杂太多了。而循环实在是太过于常见的语法了,所以一次又一次使用这种本可以简单,但是受限于语法而搞得这么复杂的C++可怜语法的时候,我总是忍不住想要吐血。对于这么简单的例子,我们是可以找到一些方法来稍微简化一点的。没有for each语法,我们起码还有for_each算法-_-!

于是可以这样:

#include <list>

#include <iostream>

#include <algorithm>

using namespace std;

 

void printInt(int i)

{

    cout <<i <<endl;

}

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    // 同样是需要输出

    for_each(l.begin(), l.end(), ptr_fun(printInt));

 

    system("PAUSE");

}

 

在加大了理解难度后(本来for each语法多简单啊,现在还要理解ptr_fun这样的函数对象生成的辅助函数),我们的循环是稍微简单一点了,虽然在这个例子中我们甚至要额外写函数-_-!虽然说函数可以只写一次,循环可是常常用的啊。

对于这样简单的例子,已经可以看出没有for_each语法的痛苦了,再复杂一点的例子

 

对于类成员函数的调用,看看有for_each的情况

python中:

class Add():
    def __init__(self, i):
        self._i = i
    def add(self):
        self._i += 1
    def __str__(self):
        return str(self._i)
    
s = [Add(1), Add(2), Add(3)]

for a in s:
    a.add()

for a in s:
    print a

 

这里拆分成两个函数,可以看出我的无奈,想要在一个for_each语法中连续调用两个函数的方法。。。。目前只有再写一个函数,而这个函数的作用就是仅仅调用这两个函数提供给for_each使用。不说这些丧气+无奈的话了,光是调用一个类的成员函数的可能还是有的。

C++中:

#include <list>

#include <iostream>

#include <algorithm>

#include <cstdio>

using namespace std;

 

void printInt(int i)

{

    cout <<i <<endl;

}

 

class CAdd

{

public:

    CAdd(int ai):mi(ai) { }

 

    void add() { ++mi; }

 

    operator int() { return mi;}

    int mi;

};

 

int main()

{

    CAdd a[3] = { CAdd(1), CAdd(2), CAdd(3)};

    list<CAdd> l(a, a+3);

 

    // 同样是需要输出

    for_each(l.begin(), l.end(), mem_fun_ref(&CAdd::add));

    for_each(l.begin(), l.end(), ptr_fun(printInt));

 

    system("PAUSE");

}

为了实现循环的简洁,重新引入了新的复杂度,mem_fun_ref,希望一般的C++程序员见过这样的函数对象辅助函数。。。。还多了类似&CAdd::add这样的成员函数指针的语法,希望一般的程序员也能理解。。。。(不提有for each语法的语言中除了for each这样自然的语法外,做多复杂的运算都没有引入任何新的复杂度),最主要的是,你想要在一条for_each中实现两个函数的调用,你除了老老实实的实现一个新的函数外,就是像我这样了,调用for_each两次,两种方法都是不那么容易让人接受。。。。。。。。但是,在现有的C++中,我们也就只能做到这样了。既然用C++,就接受现实吧。其实,显示远比一般人想象的要复杂。

以上情况还是函数没有参数的时候,当函数有参数的时候,新的问题又来了。

看看python中这样一个简单的功能:

def add(a,b):
    return a + b

l = [1,2,3,4,5]
for i in l:
    print add(i,1)

 

无非就是在每个输出的函数中调用一个函数,没有任何值的一提的地方,是个人就能看懂。

C++需要实现成下面这个样子:

#include <list>

#include <iostream>

#include <algorithm>

#include <functional>

using namespace std;

 

template <typename T>

class Add : public binary_function<T, T, void>

{

public:

    void operator()(const T& ai, const T& aj) const

    {

       cout <<(ai + aj) <<endl;

    }

 

};

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    for_each(l.begin(), l.end(), bind2nd(Add<int>(), 1));

 

    system("PAUSE");

}

 

到这一步,我希望大部分的C++程序员还能看懂什么意思及其实现的机制。。。。但是仅仅是我的希望吧,甚至我怀疑,这样的实现放在工作中,总监和老总是不是会将我批的体无完肤,的确,为了省略一个循环值得这样做吗?实在不值得,但是C++提供给你的机制就是这样。Add这样的函数对象构造复杂,还得利用trail机制(从binary_function类继承过来),然后再利用函数适配器bind2nd/bind1st,这样的东西似乎需要语言专家来解释,我是解释不清楚了,再加上更加复杂的函数连标准库中的bind都肯定不够用,还只能用boost::bind库,去试试吧,然后会发现一般的函数指来指去(特别是类成员)用的太复杂了,还是用boost::funciton吧。。。。。似乎永无止境。但是有了for each语法,那么什么复杂度都没有。。。。。还想自虐吗?算了吧,我基本上已经放弃了。不给糖吃,也放不着自己开工厂制作。。。。

另外,对于能够用boost的兄弟们,糖是有的吃的。boost:: foreach库即是如此。

 

下面是boost:: foreach的例子

#include <list>

#include <iostream>

#include <boost/foreach.hpp>

using namespace std;

 

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    BOOST_FOREACH( int i, l)

    {

       cout <<i <<endl;

    }

 

    BOOST_FOREACH( int i, l)

    {

       cout <<i+1 <<endl;

    }

 

    system("PAUSE");

}

 

就算仅仅这一个例子。。。。永远不要怪库开发者(比如boost,ace,loki)将C++语言弄得多么扭曲,他们也是出于无奈。。。。别去看实现,先只管用吧。

对于不能用boost的我。。。。只能看有没有办法偷偷的将/cli编译选项打开了。。。^^

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

Observer模式的升级版,Event通知实现

Observer模式的升级版,Event通知实现

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

本来这段程序是为mmgwg小组程序做示范写的,但是想想最近也很少有时间在家这样写程序了。。。。还是放上来吧,应该对有类似需求的兄弟们也有一些示范作用,对于程序的类层次结构就没有更多的深究了,仅仅是表达大概的意思,并不是希望大家都像我一样所有的实现都跑到父类中去。切记。

要说明的是,原始的Observer模式仅仅说明了一种思想,但是使用价值并不高,因为很简单的道理,作为一个系统基础的行为模式,其仅仅支持一种通知方式,并且是遍历通过。。。(这也可以看做模式的说明仅仅是实现了一种很通用的情况,并不是最好的实现),我们使用的也可以看作是Observer模式的延伸,也可以看做Observer模式的另一种实现,是所谓的 Event通知方式。在实际代码中使用非常多。在我们公司代码中似乎不论是服务器还是客户端代码都需要用到。服务器代码中甚至为了实现策划编辑数据另外做了一套(也就是并行的两套)。

具体的例子我打包放到

http://groups.google.com/group/jiutianfile/

中了,文件名为Event Drive sample code.rar

程序没有经过调试,仅仅是编译通过,请仅仅作为示范使用。其中唯一比较难掌握的可能就是类成员函数的指针了,但是难的主要是语法,真正的使用上没有太多问题。

Observer.h

 

#ifndef __OBSERVER_H__

#define __OBSERVER_H__

#include "BaseDefines.h"

 

class CSubject;

class CObserver

{

typedef LRESULT (CObserver:: *EventFunc)(LPARAM , WPARAM , WPARAMEX );

 

public:

    CObserver(CSubject* apSubject);

    virtual ~CObserver(void);

 

    bool InitEvent();

 

    // 此接口相当于原设计模式中的Update

    LRESULT OnEvent(EventID_t aiEventID, LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx);

 

    typedef map<EventID_t, EventFunc> EventMap_t;

    typedef EventMap_t::iterator EventIter_t;

    EventMap_t moEvents;

 

 

    // 以下仅为示例,即是各个事件的相应

    LRESULT OnEvent1( LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx);

    LRESULT OnEvent2( LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx);

    LRESULT OnEvent3( LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx);

 

    CSubject *mpSubject;

};

 

#endif

 

Observer.cpp

#include "StdAfx.h"

#include "Observer.h"

#include "Subject.h"

 

CObserver::CObserver(CSubject* apSubject):mpSubject(apSubject)

{

    InitEvent();

}

 

CObserver::~CObserver(void)

{

}

 

// 此实现中通过aiEventID分发事件,具体方式类似于MFC的消息映射

LRESULT CObserver::OnEvent( EventID_t aiEventID, LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx )

{

    EventIter_t lit = moEvents.find(aiEventID);

    if(lit == moEvents.end())

    {

       // 表示Observer实际没有订阅此事件。

       return -1;

    }

 

    // 假如有订阅,则调用相应的响应函数

    return ( this->*(lit->second))(aLParam, aWParam, aWParamEx);

}

 

bool CObserver::InitEvent()

{

    moEvents.insert(make_pair(1, &CObserver::OnEvent1));

    moEvents.insert(make_pair(2, &CObserver::OnEvent2));

    moEvents.insert(make_pair(3, &CObserver::OnEvent3));

 

    return true;

}

 

LRESULT CObserver::OnEvent1( LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx )

{

 

    return 1;

}

 

LRESULT CObserver::OnEvent2( LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx )

{

 

    return 1;

}

 

LRESULT CObserver::OnEvent3( LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx )

{

 

    return 1;

}

 

Subject.h

#ifndef __SUBJECT_H__

#define __SUBJECT_H__

 

#include "BaseDefines.h"

#include <map>

#include <list>

using namespace std;

 

class CObserver;

class CSubject

{

public:

    CSubject(void);

    virtual ~CSubject(void);

 

 

    bool Attach( EventID_t aiEventID, CObserver *apObs);

    bool Detach( EventID_t aiEventID, CObserver *apObs);

 

    // 类似原Observer模式中的Notice消息

    LRESULT SendEvent(EventID_t aiEventID, LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx );

 

    typedef list<CObserver*> ObsList_t;

    typedef ObsList_t::iterator ObsIter_t;

 

    typedef map<EventID_t, ObsList_t> EventObsMap_t;

    typedef EventObsMap_t::iterator EventObsIter_t;

    EventObsMap_t moEventObsMap;

};

 

#endif

 

Subject.cpp

#include "StdAfx.h"

#include "Subject.h"

#include "Observer.h"

#include <boost/bind.hpp>

 

CSubject::CSubject(void)

{

}

 

CSubject::~CSubject(void)

{

}

 

 

bool CSubject::Attach( EventID_t aiEventID, CObserver *apObs )

{

    EventObsIter_t lit = moEventObsMap.find(aiEventID);

    if(lit != moEventObsMap.end())

    {// 已经有相关的事件响应Obs列表

       ObsList_t& lObsList = lit->second;

       ObsIter_t litList = find(lObsList.begin(), lObsList.end(), apObs);

       if(litList != lObsList.end())

       {// 重复Attach

           return false;

       }

 

       lObsList.push_back(apObs);

       return true;

    }

 

    // 实际上先添加listmap然后再添加bos效率会略高。

    ObsList_t lObsList;

    lObsList.push_back(apObs);

 

    // insert必成功

#ifdef _DEBUG

    pair<EventObsIter_t, bool> lpairResult = moEventObsMap.insert(make_pair(aiEventID, lObsList));

    ASSERT(lpairResult.second);

#else

    moEventObsMap.insert(make_pair(aiEventID, lObsList));

#endif

 

    return true;

}

 

bool CSubject::Detach( EventID_t aiEventID, CObserver* apObs )

{

    EventObsIter_t lit = moEventObsMap.find(aiEventID);

    if(lit == moEventObsMap.end())

    {// 根本没有订阅此消息

       return false;

    }

 

    ObsList_t& lObsList = lit->second;

    ObsIter_t litList = find(lObsList.begin(), lObsList.end(), apObs);

    if(litList == lObsList.end())

    {// 根本没有订阅此消息

       return false;

    }

 

    // 这里当list为空的时候也不删除map中的对应item,添加的时候速度更快,也没有删除的时间,消耗多一点内存,但是减少内存碎片

    lObsList.erase(litList);

 

    return true;

}

 

LRESULT CSubject::SendEvent( EventID_t aiEventID, LPARAM aLParam, WPARAM aWParam, WPARAMEX aWParamEx )

{

    EventObsIter_t lit = moEventObsMap.find(aiEventID);

    if(lit == moEventObsMap.end())

    {// 根本没有Observer订阅此消息

       return 0;

    }

 

    ObsList_t& lObsList = lit->second;

    for(ObsIter_t litList = lObsList.begin(); litList != lObsList.end(); ++litList)

    {

       (*litList)->OnEvent(aiEventID, aLParam, aWParam, aWParamEx);

    }

 

    return 1;

}

 

 

 

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

序列化支持(4)—Boost的序列化库的强大之处

序列化支持(4)—Boost的序列化库的强大之处

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

1.      非介入式版本

感觉实际使用中我还没有碰到过,既然需要完全的public才能还原数据,那么介入不介入好像影响也不大了,除非碰到一个东西是别的公司写的,不让改,我是还没有碰到这样的情况。

 

从这里开始见识Boost序列化库的强大。。。。。。。。

 

2.      指针的序列化:

下面的例子为文档中例子的简化,并添加必要部分以方便运行及演示。

// BoostLearn.cpp : 定义控制台应用程序的入口点。

//

 

#include "stdafx.h"

 

#include <fstream>

 

// 包含以简单文本格式实现存档的头文件

#include <boost/archive/text_oarchive.hpp>

#include <boost/archive/text_iarchive.hpp>

 

/////////////////////////////////////////////////////////////

// gps 座标

//

// 举例说明简单类型的序列化

//

class gps_position

{

private:

    friend class boost::serialization::access;

    // 如果类Archive 是一个输出存档,则操作符& 被定义为<<.  同样,如果类Archive

    // 是一个输入存档,则操作符& 被定义为>>.

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & degrees;

       ar & minutes;

       ar & seconds;

    }

    int degrees;

    int minutes;

    float seconds;

public:

    gps_position()

    {

       degrees = 0;

       minutes = 0;

       seconds = 0.0;

    };

    gps_position(int d, int m, float s) :

    degrees(d), minutes(m), seconds(s)

    {}

};

 

class bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & latitude;

       ar & longitude;

    }

 

    gps_position latitude;

    gps_position longitude;

public:

    bus_stop(){ }

    bus_stop(const gps_position & lat_, const gps_position & long_) :

        latitude(lat_), longitude(long_){ }

        

    virtual ~bus_stop(){ }

};

 

class bus_stop_corner : public bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       // 序列化基类信息

       ar & boost::serialization::base_object<bus_stop>(*this);

       ar & street1;

       ar & street2;

    }

    std::string street1;

    std::string street2;

 

public:

    bus_stop_corner(){}

    bus_stop_corner(const gps_position & lat_, const gps_position & long_,

       const std::string & s1_, const std::string & s2_

       ) :

    bus_stop(lat_, long_), street1(s1_), street2(s2_)

    {}

 

    virtual std::string description() const

    {

       return street1 + " and " + street2;

    }

};

 

 

class bus_route

{

    friend class boost::serialization::access;

    bus_stop_corner * stops[2];

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       int i;

       for(i = 0; i < 2; ++i)

           ar & stops[i];

    }

public:

    bus_route(bus_stop_corner *apStop1, bus_stop_corner *apStop2)

    {

       stops[0] = apStop1;

       stops[1] = apStop2;

    }

    bus_route(){}

};

 

 

 

int main() {

    // 创建并打开一个输出用的字符存档

    std::ofstream ofs("bus_route");

 

    // 创建类实例

    const gps_position latitude(1, 2, 3.3f);

    const gps_position longitude(4, 5, 6.6f);

 

    bus_stop_corner *lpStop1 = new bus_stop_corner(latitude, longitude, "corn1", "corn2");

    bus_stop_corner *lpStop2 = new bus_stop_corner(latitude, longitude, "corn3", "corn4");

 

    bus_route route(lpStop1, lpStop2);

 

    // 保存数据到存档

    {

       boost::archive::text_oarchive oa(ofs);

       // 将类实例写出到存档

       oa << route;

       // 在调用析构函数时将关闭存档和流

    }

 

    // ... 晚些时候,将类实例恢复到原来的状态

    bus_route new_route;

    {

       // 创建并打开一个输入用的存档

       std::ifstream ifs("bus_route", std::ios::binary);

       boost::archive::text_iarchive ia(ifs);

       // 从存档中读取类的状态

       ia >> new_route;

       // 在调用析构函数时将关闭存档和流

    }

 

    delete lpStop1;

    delete lpStop2;

    return 0;

}

 

 

这里的强大之处在于指针反序列化的时候自动的分配了内存,这样简化了很多的操作,当然,这样就会存在文档中提出的内存泄露的问题,在此例中的确存在,反序列化时分配了内存但是却没有合理的地方去释放,由外部去释放感觉并不是太妥当,boost文档中的建议是使用智能指针,比如share_ptr。这个例子我们放到最后,先看看利用普通类的一条确保内存分配并不泄露的原则,哪里分配的哪里释放,对象自己管理自己的内存。

见下例:(此例为文档中没有的)

 

// BoostLearn.cpp : 定义控制台应用程序的入口点。

//

 

#include "stdafx.h"

 

#include <fstream>

 

// 包含以简单文本格式实现存档的头文件

#include <boost/archive/text_oarchive.hpp>

#include <boost/archive/text_iarchive.hpp>

 

/////////////////////////////////////////////////////////////

// gps 座标

//

// 举例说明简单类型的序列化

//

class gps_position

{

private:

    friend class boost::serialization::access;

    // 如果类Archive 是一个输出存档,则操作符& 被定义为<<.  同样,如果类Archive

    // 是一个输入存档,则操作符& 被定义为>>.

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & degrees;

       ar & minutes;

       ar & seconds;

    }

    int degrees;

    int minutes;

    float seconds;

public:

    gps_position()

    {

       degrees = 0;

       minutes = 0;

       seconds = 0.0;

    };

    gps_position(int d, int m, float s) :

    degrees(d), minutes(m), seconds(s)

    {}

};

 

class bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & latitude;

       ar & longitude;

    }

 

    gps_position latitude;

    gps_position longitude;

public:

    bus_stop(){ }

    bus_stop(const gps_position & lat_, const gps_position & long_) :

        latitude(lat_), longitude(long_){ }

        

    virtual ~bus_stop(){ }

};

 

class bus_stop_corner : public bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       // 序列化基类信息

       ar & boost::serialization::base_object<bus_stop>(*this);

       ar & street1;

       ar & street2;

    }

    std::string street1;

    std::string street2;

 

public:

    bus_stop_corner(){}

    bus_stop_corner(const gps_position & lat_, const gps_position & long_,

       const std::string & s1_, const std::string & s2_

       ) :

    bus_stop(lat_, long_), street1(s1_), street2(s2_)

    {}

 

    virtual std::string description() const

    {

       return street1 + " and " + street2;

    }

};

 

 

class bus_route

{

    friend class boost::serialization::access;

 

    // 这里将数组缩减到,是为了减少编码量并显得更清楚,毕竟说明清楚情况就好了

    bus_stop_corner * stops[2];

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       for(int i = 0; i < 2; ++i)

           ar & stops[i];

    }

public:

    bus_route(const bus_stop_corner& aoStop1, const bus_stop_corner& aoStop2)

    {

       stops[0] = new bus_stop_corner(aoStop1);

       stops[1] = new bus_stop_corner(aoStop2);

    }

 

    bus_route()

    {

       stops[0] = new bus_stop_corner;

       stops[1] = new bus_stop_corner;

    }

 

    ~bus_route()

    {

       for(int i = 0; i < 2; ++i)

       {

           delete stops[i];

       }

    }

};

 

 

 

int main() {

    // 创建并打开一个输出用的字符存档

    std::ofstream ofs("bus_route");

 

    // 创建类实例

    const gps_position latitude(1, 2, 3.3f);

    const gps_position longitude(4, 5, 6.6f);

 

    bus_stop_corner loStop1(latitude, longitude, "corn1", "corn2");

    bus_stop_corner loStop2(latitude, longitude, "corn3", "corn4");

 

    bus_route route(loStop1, loStop2);

 

    // 保存数据到存档

    {

       boost::archive::text_oarchive oa(ofs);

       // 将类实例写出到存档

       oa << route;

       // 在调用析构函数时将关闭存档和流

    }

 

    // ... 晚些时候,将类实例恢复到原来的状态

    bus_route new_route;

    {

       // 创建并打开一个输入用的存档

       std::ifstream ifs("bus_route", std::ios::binary);

       boost::archive::text_iarchive ia(ifs);

       // 从存档中读取类的状态

       ia >> new_route;

       // 在调用析构函数时将关闭存档和流

    }

    return 0;

}

 

 

其实在一般情况下,只需要遵循了上述的原则,内存泄露问题一般不会存在,但是这里有个疑问就是,当指针分配了内存,boost序列化的时候是不是还是傻傻的去重新分配一次内存,然后导致第一次分配的内存没有正常释放,导致内存泄露呢?我们来检验一样。

new_route调用默认构造函数分配内存时,数组中指针的地址如下:

[0] = 0x003b7090 {street1="" street2="" }

[1] = 0x003bafb0 {street1="" street2="" }

 

反序列化后:

[0] = 0x003b9150 {street1="corn1" street2="corn2" }

[1] = 0x003b9268 {street1="corn3" street2="corn4" }

经证实。。。。。boost在指针已经分配过内存的情况下仍然重新为指针分配了一次内存,这一点够傻的,那么,这样傻的行为有没有一点保护呢?比如首先判断一下指针是否为NULL,然后先deletenew呢?虽然这样的操作好像更傻。。。当时总好过与内存泄露,验证一下的方法很简单,在ia >> new_route;一句执行前在bus_stop_corner的析构函数上加断电,假如boost调用了delete,其析构一定会发生,事实是残酷的。。。。假如事先为指针分配了内存,那么必然发生内存的泄露。。。。boost根本不管指针是否已经分配过内存,直接忽略,并重新分配内存。

 

其实,一般而言,需要反序列化的时候,提供一个空的对象也是比较合理的,毕竟这是一个还原对象的过程,所以程序改成下面例子这样就可以在不使用智能指针的时候避免内存泄露了。

 

// BoostLearn.cpp : 定义控制台应用程序的入口点。

//

 

#include "stdafx.h"

 

#include <fstream>

 

// 包含以简单文本格式实现存档的头文件

#include <boost/archive/text_oarchive.hpp>

#include <boost/archive/text_iarchive.hpp>

#include <cstdlib>

using namespace std;

 

/////////////////////////////////////////////////////////////

// gps 座标

//

// 举例说明简单类型的序列化

//

class gps_position

{

private:

    friend class boost::serialization::access;

    // 如果类Archive 是一个输出存档,则操作符& 被定义为<<.  同样,如果类Archive

    // 是一个输入存档,则操作符& 被定义为>>.

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & degrees;

       ar & minutes;

       ar & seconds;

    }

    int degrees;

    int minutes;

    float seconds;

public:

    gps_position()

    {

       degrees = 0;

       minutes = 0;

       seconds = 0.0;

    };

    gps_position(int d, int m, float s) :

    degrees(d), minutes(m), seconds(s)

    {}

};

 

class bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & latitude;

       ar & longitude;

    }

 

    gps_position latitude;

    gps_position longitude;

public:

    bus_stop(){ }

    bus_stop(const gps_position & lat_, const gps_position & long_) :

        latitude(lat_), longitude(long_){ }

        

    virtual ~bus_stop(){ }

};

 

class bus_stop_corner : public bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       // 序列化基类信息

       ar & boost::serialization::base_object<bus_stop>(*this);

       ar & street1;

       ar & street2;

    }

    std::string street1;

    std::string street2;

 

public:

    bus_stop_corner(){}

    bus_stop_corner(const gps_position & lat_, const gps_position & long_,

       const std::string & s1_, const std::string & s2_

       ) :

    bus_stop(lat_, long_), street1(s1_), street2(s2_)

    {}

 

    virtual std::string description() const

    {

       return street1 + " and " + street2;

    }

 

    ~bus_stop_corner()

    {

    }

};

 

 

class bus_route

{

    friend class boost::serialization::access;

 

    // 这里将数组缩减到,是为了减少编码量并显得更清楚,毕竟说明清楚情况就好了

    bus_stop_corner * stops[2];

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       for(int i = 0; i < 2; ++i)

           ar & stops[i];

    }

public:

    bus_route(const bus_stop_corner& aoStop1, const bus_stop_corner& aoStop2)

    {

       stops[0] = new bus_stop_corner(aoStop1);

       stops[1] = new bus_stop_corner(aoStop2);

    }

 

    bus_route()

    {

       stops[0] = NULL;

       stops[1] = NULL;

    }

 

    ~bus_route()

    {

       for(int i = 0; i < 2; ++i)

       {

           if(stops[i] != NULL)

           {

              delete stops[i];

           }

       }

    }

};

 

 

 

int main() {

    // 创建并打开一个输出用的字符存档

    std::ofstream ofs("bus_route");

 

    // 创建类实例

    const gps_position latitude(1, 2, 3.3f);

    const gps_position longitude(4, 5, 6.6f);

 

    bus_stop_corner loStop1(latitude, longitude, "corn1", "corn2");

    bus_stop_corner loStop2(latitude, longitude, "corn3", "corn4");

 

    bus_route route(loStop1, loStop2);

 

    // 保存数据到存档

    {

       boost::archive::text_oarchive oa(ofs);

       // 将类实例写出到存档

       oa << route;

       // 在调用析构函数时将关闭存档和流

    }

 

    // ... 晚些时候,将类实例恢复到原来的状态

    bus_route new_route;

    {

       // 创建并打开一个输入用的存档

       std::ifstream ifs("bus_route", std::ios::binary);

       boost::archive::text_iarchive ia(ifs);

       // 从存档中读取类的状态

       ia >> new_route;

       // 在调用析构函数时将关闭存档和流

    }

    return 0;

}

 

 

这里的bus_route类有指针和内存分配但是没有合理的拷贝构造函数和operator=重载,仅仅只是作为演示使用,实际中这里几乎是必须的,即使不需要使用到复制也应该将此两个函数放入private中以表示禁止复制,以防误用。(比如stdI/O stream类实现即是如此)

 

改成上述例子中的形式后,需要注意的是反序列化前一定要是一个空对象,假如以前有分配内存的话需要提前释放到,还好这些都可以很简单的由对象本身所保证。这一点可能的错误应用应该算是Boost为了易用性而导致的。。。。基本掌握了还算能接受,起码对于指针的序列化还是简单了很多,仅仅是需要多注意一下传入的必须是空的指针就行。

 

Boost作为准标准库,虽然有的时候显得有点庞大,但是STL的搭配,和众多新Boost特性的搭配是非常的默契(不知道这个词是否恰当)。从上面的序列化就可以看出来,序列化是完全融入原有的C++ stream体系的,这点我们公司的序列化根本没有办法比。谈到这点就是想说,其实包括auto_ptr甚至shared_ptr在内的智能指针,包括vector,mapSTL容器,甚至连array在内。

 

这里是使用智能指针的两个例子,但是boostserialize库如此偏心。。。。。share_ptr是内嵌在库里面的,而C++标准库的auto_ptr竟然没有内嵌在库里面,仅仅是在demo中给出实现。。。。。。也就是说,明明实现了,就是不想放到库里面去。。。。。如此不推荐使用auto_ptr的做法,完全见证了我当时强烈感叹的auto_ptr的异类。。。。我就不知道他是怎么混进标准库的。

 

以下是智能指针的使用示例代码:

// BoostLearn.cpp : 定义控制台应用程序的入口点。

//

 

#include "stdafx.h"

 

#include <fstream>

 

// 包含以简单文本格式实现存档的头文件

#include <boost/archive/text_oarchive.hpp>

#include <boost/archive/text_iarchive.hpp>

#include <cstdlib>

#include <boost/tr1/memory.hpp>

#include <vector>

#include <map>

#include <boost/tr1/unordered_map.hpp>

#include <memory>

#include <boost/serialization/shared_ptr.hpp>

using namespace std;

using namespace boost;

using namespace boost::serialization;

 

#include <boost/serialization/split_free.hpp>

 

namespace boost {

    namespace serialization {

 

       /////////////////////////////////////////////////////////////

       // implement serialization for auto_ptr<T>

       // note: this must be added to the boost namespace in order to

       // be called by the library

       template<class Archive, class T>

       inline void save(

           Archive & ar,

           const std::auto_ptr<T> &t,

           const unsigned int file_version

           ){

              // only the raw pointer has to be saved

              // the ref count is rebuilt automatically on load

              const T * const tx = t.get();

              ar << tx;

       }

 

       template<class Archive, class T>

       inline void load(

           Archive & ar,

           std::auto_ptr<T> &t,

           const unsigned int file_version

           ){

              T *pTarget;

              ar >> pTarget;

              // note that the reset automagically maintains the reference count

#if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, == 1)

              t.release();

              t = std::auto_ptr<T>(pTarget);

#else

              t.reset(pTarget);

#endif

       }

 

       // split non-intrusive serialization function member into separate

       // non intrusive save/load member functions

       template<class Archive, class T>

       inline void serialize(

           Archive & ar,

           std::auto_ptr<T> &t,

           const unsigned int file_version

           ){

               boost::serialization::split_free(ar, t, file_version);

       }

 

    } // namespace serialization

} // namespace boost

 

 

/////////////////////////////////////////////////////////////

// gps 座标

//

// 举例说明简单类型的序列化

//

class gps_position

{

private:

    friend class boost::serialization::access;

    // 如果类Archive 是一个输出存档,则操作符& 被定义为<<.  同样,如果类Archive

    // 是一个输入存档,则操作符& 被定义为>>.

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & degrees;

       ar & minutes;

       ar & seconds;

    }

    int degrees;

    int minutes;

    float seconds;

public:

    gps_position()

    {

       degrees = 0;

       minutes = 0;

       seconds = 0.0;

    };

    gps_position(int d, int m, float s) :

    degrees(d), minutes(m), seconds(s)

    {}

};

 

class bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & latitude;

       ar & longitude;

    }

 

    gps_position latitude;

    gps_position longitude;

public:

    bus_stop(){ }

    bus_stop(const gps_position & lat_, const gps_position & long_) :

        latitude(lat_), longitude(long_){ }

        

    virtual ~bus_stop(){ }

};

 

class bus_stop_corner : public bus_stop

{

    friend class boost::serialization::access;

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       // 序列化基类信息

       ar & boost::serialization::base_object<bus_stop>(*this);

       ar & street1;

       ar & street2;

    }

    std::string street1;

    std::string street2;

 

public:

    bus_stop_corner(){}

    bus_stop_corner(const gps_position & lat_, const gps_position & long_,

       const std::string & s1_, const std::string & s2_

       ) :

    bus_stop(lat_, long_), street1(s1_), street2(s2_)

    {}

 

    virtual std::string description() const

    {

       return street1 + " and " + street2;

    }

 

    ~bus_stop_corner()

    {

    }

};

 

 

class bus_route

{

    friend class boost::serialization::access;

 

    // 这里将数组缩减到,是为了减少编码量并显得更清楚,毕竟说明清楚情况就好了

    shared_ptr<bus_stop_corner> msptrBusStop;

    auto_ptr<bus_stop_corner> maptrBusStop;

 

    template<class Archive>

    void serialize(Archive & ar, const unsigned int version)

    {

       ar & msptrBusStop;

       ar & maptrBusStop;

    }

public:

    bus_route(const bus_stop_corner& aoStop1, const bus_stop_corner& aoStop2):

      msptrBusStop(new bus_stop_corner(aoStop1)),

       maptrBusStop(new bus_stop_corner(aoStop2))

    {

    }

 

    bus_route()

    {

 

    }

 

    ~bus_route()

    {

    }

};

 

 

 

int main() {

    // 创建并打开一个输出用的字符存档

    std::ofstream ofs("bus_route");

 

    // 创建类实例

    const gps_position latitude(1, 2, 3.3f);

    const gps_position longitude(4, 5, 6.6f);

 

    bus_stop_corner loStop1(latitude, longitude, "corn1", "corn2");

    bus_stop_corner loStop2(latitude, longitude, "corn3", "corn4");

 

    bus_route route(loStop1, loStop2);

 

    // 保存数据到存档

    {

       boost::archive::text_oarchive oa(ofs);

       // 将类实例写出到存档

       oa << route;

       // 在调用析构函数时将关闭存档和流

    }

 

    // ... 晚些时候,将类实例恢复到原来的状态

    bus_route new_route;

    {

       // 创建并打开一个输入用的存档

       std::ifstream ifs("bus_route", std::ios::binary);