??xml version="1.0" encoding="utf-8" standalone="yes"?> 赖勇(http://laiyonghao.comQ?br /> 声明Q本文源?Danny Kalev ?2011 q?6 ?21 日发表的《The Biggest Changes in C++11(and Why You Should Care)》一文,几乎所有内定w搬了q来Q但不是全文照译Q有困惑之处Q请参详原文Q?a >http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/ Q?br /> 注:作?Danny Kalev 曾是 C++ 标准委员会成员?/p> Lambda 表达式的形式是这L(fng)Q?br /> ?C++03 中,声明对象的同时必L明其cdQ其实大多数情况下,声明对象的同时也会包括一个初始|C++11 在这U情况下p够让你声明对象时不再指定cd了: C++ 最有 4 U不同的初始化Ş式,如括号内初始化,见: 像以下Ş式的函数Q?br /> nullptr 是一个新?C++ 关键字,它是I指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强cd的: C++11 中构造函数可以调用同一个类的另一个构造函敎ͼ ?C++03 中的引用cd是只l定左值的QC++11 引用一个新的引用类型叫叛_引用类型,它是l定到右值的Q如临时对象或字面量?br /> 增加叛_引用的主要原因是ؓ了实?move 语义。与传统的拷贝不同,move 的意思是目标对象“H取”原对象的资源Qƈ源|于“I?#8221;状态。当拯一个对象时Q其实代h贵且无必要,move 操作可以替代它。如? string 交换的时候,使用 move 意义有巨大的性能提升Q如原方案是q样的: ?TR1 包含的新容器Qunordered_set, unordered_map, unordered_multiset, 和unordered_multimapQ,q有一些新的库Q如正则表达式,tupleQ函数对象封装器{。下面介l一?C++11 的标准库新特性: 从程序员的角度来看,C++11 最重要的特性就是ƈ发了。C++11 提供?thread c,也提供了 promise ?future 用以q发环境中的同步Q用 async() 函数模板执行q发dQ和 thread_local 存储声明为特定线E独占的数据Q这里(http://www.devx.com/SpecialReports/Article/38883Q有一个简??C++11 U程库教E(英文Q?/p> C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用,C++11 引入了新的智能针对类 shared_ptr ?unique_ptr。它们都是标准库的其它组件兼容,可以安全地把指针存入标准容器Q也可以安全地用标准法“倒腾”它们?/p> 主要?all_of()、any_of() ?none_of()Q下面是例子Q?br /> Function/bind可以是一个很单的话题Q因为它其实不过是一个泛型的函数指针。但是如果那么来谈,没意思了Q也犯不上写q篇东西。在我看来,q个事情要讲的话Q就应该讲透,讲到回调QcallbackQ、代理(delegateQ、信PsignalQ和消息传递(messagingQ的层面Q因为它实是太重要了。这个话题不但与面向对象的核心思想密切相关Q而且是面向对象两大流z之间交锋的中心。围l这个问题的思考和争论Q几乎把20q来所有主的~程q_和编E语a都搅q来了。所以,如果详尽铺陈Q这个话题直接可以写一本书?/p>
写书我当然没那个水^Q但q个题目实一直想动一动。然而这个主题实在太大,我实在没有精力把它完整的写下来;q个主题也很深,特别是涉及到q发环境有关的话题,我的理解q非常肤,总觉得我认识的很多高手都比我更有资格写这个话题。所以犹豫了很久Q要不要现在写,该怎么写。最后我觉得Q确实不能把一博客文章写成一?0q面向对象技术史讎ͼ所以决定保留大的架构,但是对其中具体的技术细节点Cؓ止。我不会去详l地列D代码Q分析对象的内存布局Q画C意图,但是会把最重要的结论和观点写下来,说得好听一Ҏ(gu)提纲挈领Q说的不好听是语焉不详。但无论如何Q我惌样一东西,一是谈谈我对这个事情的看法Q二?#8220;抛砖引玉”Q引来高手的xQ引出更深刻和完整的叙述?/p>
下面开始?/p>
0. E序设计有一个范式(paradigmQ问题。所谓范式,是l织E序的基本思想Q而这个基本思想Q反映了E序设计者对E序的一个基本的哲学观,也就是说Q他认ؓE序的本质是什么,他认Z个大的程序是׃么组成的。而这Q又跟他对于现实世界的看法有兟뀂显Ӟq样的看法不可能有很多种。编E作Z门行业,独立存在?0q了Q但是所出现的范式不q三U——过E范式、函数范式、对象范式。其中函数范式与现实世界差距比较大,在这里不讨论。而过E范式和对象范式可以视ؓ对程序本质的两种Ҏ(gu)不同的看法,而且能够分别在现实世界中扑ֈ相应的映?br>q程范式认ؓQ程序是׃个又一个过E经q顺序、选择和@环的l构l合而成。反映在现实世界Q过E范式体C力_分工之前“全能?#8221;的工作特点——所有的事情都能qԌ所有的资源都是我的Q只不过得具体的事情得一步步地来做? 对象范式与过E范式相比,有三个突出的优势Q第一Q由于实C逻辑上的分工Q降低了大规模程序的开发难度。第二,灉|性更好——若q对象在一P可以灉|l合Q可以以不同的方式协作,完成不同的Q务,也可以灵zȝ替换和升U。第三,对象范式更加适应囑Ş化、网l化、消息驱动的C计算环境?/p>
所以,较之于过E范式,对象范式Q或者说“面向对象”Q确实是更具优势的编E范式。最q看C些文章抨击面向对象,说面向对象是胡扯Q我认ؓ要具体分析。对面向对象的一部分批评Q是冲着L?#8220;面向对象”语言ȝQ这实是有道理的,我在下面也会谈到Q而且会骂得更狠。而另一个批评的声音Q主要而来自STL之父Alex StepanovQ他说的当然有他的道理,不过要知道该牛h是前苏联莫斯U国立罗蒙诺索夫大学数学pd士,你只要翻d苏联的大学数学教材就知道了,能够在莫大拿到数学博士的Q根本就是披着人皮的外星高{智慧。而我们编写地球上的程序,可能q是应该以地球h的观点ؓ丅R?/p>
1. 重复一遍对象范式的两个基本观念Q?br>E序是由对象l成的; h意,q两个观念与后来我们熟知的面向对象三要素“装、ѝ多?#8221;Ҏ(gu)不在一个层面上Q倒是与再后来?#8220;lg、接?#8221;合?/p>
2. 世界上第一个面向对象语a是Simula-67Q第二个面向对象语言是Smalltalk-71。Smalltalk受到了Simula-67的启发,基本出发点相同,但也有重大的不同。先说相同之处,Simula和Smalltalk都秉承上q对象范式的两个基本观念Qؓ了方便对象的构造,也都引入了类、承等概念。也是_cR承这些机制是Z实现对象范式原则而构造出来的W二位的、工h的机制Q那么ؓ什么后来这些第二位的东西篡了主位,后面我会再来分析。而Simula和Smalltalk最重大的不同,是Simula用方法调用的方式向对象发送消息,而Smalltalk构造了更灵zd更纯_的消息发送机制?/p>
具体的说Q向一个Simula对象中发送消息,是调用q个对象的一个方法,或者称成员函数。那么你怎么知道能够在这个对象上调用q个成员函数呢?或者说Q你怎么知道能够向这个对象发送某个消息呢Q这p求你必须保q个对象h合适的cdQ也是_你得先知道哦q个对象是什么,才能向它发消息。而消息的实现方式被直接处理ؓ成员函数调用Q或虚函数调用?/p>
而Smalltalk在这一点上做了一个历史性的跨越Q它实现了一个与目标对象无关的消息发送机Ӟ不管那个对象是谁Q也不管它是不是能正的处理一个消息,作ؓ发送消息的对象来说Q可以毫无顾忌地抓住一个对象就发消息过厅R接到消息的对象Q要试理解q个消息Qƈ最后调用自qq程来处理消息。如果这个消息能被处理,那个对象自然会处理好Q如果不能被处理QSmalltalkpȝ会向消息的发送者回传一个doesNotUnderstand消息Q予以通知。对象不用关心消息是如何传递给另一个对象的Q传递过E被分离出来Q而不是像Simula那样明确地被以成员函数调用的方式实现Q,可以是在内存中复Ӟ也可以是q程间通讯。到了Smalltalk-80Ӟ消息传递甚臛_以跨网l?/p>
Z方便后面的讨论,不妨把源自Simula的消息机制称?#8220;静态消息机?#8221;Q把源自Smalltalk的消息机制称?#8220;动态消息机?#8221;?/p>
Simula与Smalltalk之间对于消息机制的不同选择Q主要是因ؓ两者于用途。前者是用于仿真E序开发,而后者用于图形界面环境构建,看上d自合情合理。然而,是q么一点简单的区别Q却造成了巨大的历史后果?/p>
3. C1980q代QC++出现了。Bjarne Stroustrup在博士期间深入研I过SimulaQ非常欣赏其思想Q于是就在C语言语法的基之上Q几乎把Simula的思想照搬q来QŞ成了最初的C++。C++问世以之初,主要用于解决规模E大的传l类型的~程问题Q迅速取得了巨大的成功,也证明了对象范式本n所h的威力?/p>
大约在同期,Brad CoxҎ(gu)Smalltalk的思想设计了Objective-CQ可是由于其语法怪异Q没有流行v来。只有Steve Jobsq种h宗学鉴赏力的世外高hQ把它奉为瑰宝,?988q连锅把Objective-C的团队和产品一口气C下来?/p>
4. 在同一时期QGUI成ؓ热门。虽然GUI的本质是对象范型的,但是当时Q?980q代中期Q的面向对象语言Q包括C++语言Q还q不成熟Q因此最初的GUIpȝ无一例外是用C和汇~语a开发的。或者说Q最初的GUI开发者硬是用抽象U别更低的语a构造了一个面向对象系l。熟(zhn)Win32 SDK开发的人,应该知道我在说什么?/p>
5. 当时很多Z为,如果C++更成熟些Q直接用C++来构造Windowspȝ会大大地Ҏ(gu)。也有h觉得Q尽Windowspȝ本n使用C写的Q但是其面向对象的本质与C++更契合,所以在其基上包装一个C++的GUI framework一定是轻而易举。可是一动手Z发玎ͼ完全不是那么回事。用C++开发Windows框架隑־要死。ؓ什么呢Q主要就是Windowspȝ中的消息机制实际上是动态的Q与C++的静态消息机制根本配合不C起去。在Windows里,你可以向M一个窗口发送消息,q个H口自己会在自己的wndproc里来处理q个消息Q如果它处理不了Q就交给default window/dialog procd理。而在C++里,你要向一个窗口发消息Q就得确保这个窗口能处理q个消息Q或者说Q具有合适的cd。这样一来的话,׃D一个错l复杂的H口cdơ结构,无法实现。而如果你要让所有的H口c都能处理所有可能的消息Q且不论q样在逻辑上就行不通(用户定义的消息怎么处理Q)Q单在实C׃可接受——ؓ一个小的不同得创造一个新的窗口类Q每一个小的H口c都要背上一个多达数N的v-tableQ而其中可?9%的项都是费Q不要说在当Ӟ是在今天,内存数量非常丰富的时候,如果每一个GUIE序都这么搞Q用户也吃不消?/p>
6. 实际上C++的静态消息机制还引v了更׃重的问题——扭曲了Z寚w向对象的理解。既然必要先知道对象的cdQ才能向对象发消息,那么“c?#8221;q个概念q别重要了Q而对象只不过是类q个模子里造出来的东西Q反而不重要。渐渐的Q?#8220;面向对象~程”变成?#8220;面向cȝE?#8221;Q?#8220;面向cȝE?#8221;变成?#8220;构造类l承?#8221;。放在眼前的鲜活的对象活动不重要了,反而是其背后的静态类型系l成为关键?#8220;装、?#8221;q些W二{的Ҏ(gu),喧宾ZQ俨然成了面向对象的要素。每个程序员g都要先成为领域专Ӟ然后成ؓ领域分类学专Ӟ然后构造一个完整的l承?wi),然后才能new出对象,让程序跑h。正是因个过E太漫长Q太困难Q再加上C++本n的复杂度很大,所以C++出现q么多年Q真正堪U经典的面向对象cd和框Ӟ几乎屈指可数。很多流行的库,比如MFC、iostreamQ都暴露Z问题。一般程序员总觉得是自己的水q不够,于是下更大功夫去l剑。殊不知Ҏ(gu)上是方向错了Q脱M对象范式的本质,企图用静态分cL来对现实世界建模Q去ȝ变化万千的动态世界。这么难的事Q你水^再高也很隑ց好?/p>
可以从一个具体的例子来理解这个道理,比如在一个GUIpȝ里,一?Push Button 的设计问题。事实上在一个实际的E序里,一?push button 到底“是不?#8221;一?buttonQ进而是不是一?window/widgetQƈ不重要,本质上我Ҏ(gu)不关心它是什么,它从属于哪一个类Q在l承?wi)里处于什么位|,只要那里有这么一个东西,我可以点它,点完了可以发生相应的效果Q就可以了。可是Simula –> C++ 所鼓励的面向对象设计风|非要上来想清楚Qa Push Button is-a Button, a Button is-a Command-Target Control, a Command-Target Control is-a Control, a Control is-a Window. 把这一圈都想透彻之后Q才?new 一?Push ButtonQ然后才能让它工作。这Ş而上学了Q这pd际了。所以很隑ց好。你看到 MFC 的类l承?wi),觉得设计者太牛了Q能把这些层ơ概念都x楚,自己的水q不够Q还得修点{实际上呢,q个设计是经q数不清的失败和q出来、砸出来的,MFC的前w?Afx 不是失败了吗?1995q还有一个叫?Taligent 的大目Q召集了包括 Eric Gamma 在内的一大堆牛hQ要用C++做一个一l天下的application frameworkQ最后也以惨败告l,q公叔R倒闭了,CEO车祸w亡Q牛Z(zhn)数遣散。附带说一下,q个Taligent目是ؓ了跟NextSTEP和Microsoft Cairo竞争Q前者用Objective-C~写Q后来发展ؓCocoaQ后者用传统的Win32 + COM作ؓ基础架构Q后来发展ؓWindows NT。而Objective-C和COMQ恰恰就在动态消息分z方面,与C++q然不同。后面还会谈到?/p>
客观地说Q?#8220;面向cȝ设计”q不是没有意义。来源于实践又高于实늚抽象和概念,往往能更有力地把握住现实世界的本质,比如MVC架构Q就是这L(fng)有力的抽象。但是这U抽象,应该是来源于长期最?jng)_늚ȝ和提高,而不是面寚w题时主要的解x\。过于强调这U抽象,无异于假定程序员各个都是哲学Ӟh对现实世界准而深ȝ抽象能力Q当然是不符合实际情늚。结果呢Q刚学习(fn)面向对象没几天的E序员,对眼前鲜zȝ对象世界视而不见,一个个都煞有介事地L哲学冥想Q企图越q现实世界,L象出其背后本质,当然败得很惨?/p>
其实C++问世之后不久Q这个问题就暴露出来了。第一个C++~译?Cfront 1.0 是单l承Q而到?Cfront 2.0Q加入了多ѝؓ什么?是因ؓ使用中h们发现逻辑上似乎完的静态单l承关系Q碰到复杂灵zȝ现实世界Q就破熾癑և——蝙蝠是鸟也是兽Q水上飞飞也能游Q它们该如何归类呢?本来q应该促使大家反思承这个机制本w,但是那个时候全世界陷入l承狂热Q于是就开始给l承打补丁,加入多承,q而加入虚l承Q。到了虚l承Q明gh一看便知,q只是一个语法补丁,是ؓ了逃避职责而制造的一块无用的遮羞布,它已l完全已l脱d践了——有谁在事前能够判断是否应该对基c进行虚l承呢? C1990q代中期Q问题已l十分明显。UML中有一个对象活动图Q其描述的就是运行时对象之间怺传递消息的模型?994qRobert C. Martin在《Object-Oriented C++ Design Using Booch Method》中Q曾面向对象设计从对象活动图入手Q而不是从cd入手。?995q出版的l典作品《Design Patterns》中Q徏议优先考虑l合而不是承,q也是尽人皆知的事情。这些迹象表明,在那个时候,面向对象C里的思想领袖们,已经意识?#8220;面向cȝ设计”q不好用。只可惜他们的革命精还不够?/p>
7. 你可能要问,Java ?NET也是用承关pȝl类库,q进行设计的啊,怎么那么成功呢?q里有三点应该注意。第一QC++的难不仅仅在于其静态结构体p,q有很多源于语言设计上的包袱Q比如对C的兼容,比如没有垃圾攉机制Q比如对效率的强调,{等。一旦把q些包袱丢掉Q设计的隑ֺ实可以大大下降。第二,Java?NET的核心类库是在C++十几q成功和p|的经验教训基之上Q结合COM体系优点设计实现的,自然要好上一大块。事实上Q在Java?NET核心cd的设计中很多地方Q体现的是基于接口的设计Q和真正的基于对象的设计。有了这两个主角站台Q?#8220;面向cȝ设计”不能喧宾ZQ也能发挥一些好的作用。第三,如后文指出,Java?NET中分别对C++最大的问题——缺对象别的delegate机制做出了自q回应Q这大大I补了原来的问题?/p>
管如此QJavaq是沾染上了“面向c设?#8221;的癌症,基础cd里就有很多架床叠屋的设计Q而J2EE/Java EE当中Q这UŞ而上学的设计也很普遍Q所以也引发了好几次轻量化的q动。这斚w我ƈ不是太懂Q可能需要真正的Java高手出来现n说法。我对Java的看法以前就讲过——^台和语言核心非常好,但风气不好,崇尚华丽J复的设计,装牛逼的人太多?/p>
至于.NETQ我听陈榕介l过Q在设计.NET的时候,微Y内部对于是否允许l承爆发了非常激烈的争论。很多资深高人都强烈反对l承。至于最后引入承,很大E度上是营销需要压倒了技术理性。尽如此,׃有COM的基Q又实现了非常彻底的delegateQ所?.NET 的设计水q是很高的。它的主要问题不在这Q在于太急于求胜Q更新速度太快Q基不牢。当ӞҎ(gu)问题q是微Y没有能够在Web和Mobile领域里占到多大的优势Q也׃?NET没有用武之地?/p>
8. COM。COM的要义是QY件是由COM Componentsl成Qcomponents之间彼此通过接口怺通讯。这是否让你回想h文开所提出的对象范型的两个基本原则Q有的是,在COM的术语里Q?#8220;COM Component ” ?#8220;object ”通假Q这׃COM的心思昭然若揭了。Don Box在Essential COM里开就_COM是更好的C++Q事实上是告诉大家QŞ而上学的“面向c设?#8221;不好使,q是回到对象吧?/p>
用COM开发的时候,一个组?#8220;是什?#8221;不重要,它具有什么接口,也就是说Q能够对它发什么消息,才是重要的。你可以用IUnknown::QueryInterface问组件能对哪一l消息作出反应。向lg分派消息也不一定要被绑定在Ҏ(gu)调用上,如果实现?IDispatchQ还可以实现“自动?#8221;调用Q也是COM术语里的 AutomationQ而通过 列集QmashalQ,可以跨进E、跨|络向另一lg发送消息,通过 monikerQ可以在分布式系l里定位和发现组件。如果你q“对象——消?#8221;的观念去看COM的设计,׃意识刎ͼ整个COM体系是用规范如何做对象Q如何发消息的。或者更直白一点,COM是用C/C++是模拟Z个Smalltalk。而且COM的概念世界里没有l承Q就其纯z性而言Q比SmalltalkqSmalltalk。在对象泛型上,COM辑ֈ了一个高峎ͼ领先于那个时代,甚至于比它的lQ.NETq要U洁?/p>
COM的主要问题是它的学习(fn)隑ֺ和安全问题,而且Q它q于q求U洁性,完全攑ּ?#8220;面向c设?#8221; 的机Ӟ昑־有点q?/p>
9. 好像有点扯远了,其实q是在说正事。上面说到由于C++的静态消息机ӞD了Ş而上学的“面向cȝ设计”Q祸xI但实际上,C++是有一个补救机会的Q那是实现对象U别的delegate机制。学q?NET的hQ一听delegateq个词就知道是什么意思,但Java里没有对应机制。在C++的术语体p里Q所谓对象别delegateQ就是一个对象回调机制。通过delegateQ一个对象A可以把一个特定工作,比如处理用户的鼠标事Ӟ委托l另一个对象B的一个方法来完成。A不必知道B的名字,也不用知道它的类型,甚至都不需要知道B的存在,只要求B对象h一个签名正的Ҏ(gu)Q就可以通过delegate把工作交lB的这个方法来执行。在C语言里,q个机制是通过函数指针实现的,所以很自然的,在C++里,我们希望通过指向成员函数的指针来解决cM问题?/p>
然而就在这个问题上QC++让hD痛惜?/p>
q段内容大致讲的是关于模板中的命名决议:“scope of the template definition”?#8220;scopy of the template instantition”?/span>
坦白的说我是W一ơ看到这一知识点,在上机验证后又回惟뀊C++ Primer?4th中可曾有提及q,后来发现4th中没有,倒是3rd中有专门的一节专门陈述了下?br />
后来我在《深度探索C++对象模型》书中关于模板中命名册内容的旁边写下了q样一D话Q?br />
今天才发现还有这么个东西。我只能承认自己的无知与C++的变态,面对q一知识炏V?br /> 2012.5.2 ?br />
]]>Lambda 表达?/h2>
来看个计数某个字W序列中有几个大写字母的例子Q?br />
其中 [&Uppercase] 中的 & 的意义是 lambda 函数体要获取一?Uppercase 引用Q以便能够改变它的|如果没有 &Q那?Uppercase 以传值的形式传递过厅R?自动cd推导?decltype
q个Ҏ(gu)在对象的类型很大很长的时候很有用Q如Q?br />
那个q代器可以声明ؓQ?br />
C++11 也提供了从对象或表达式中“俘获”cd的机Ӟ新的操作W?decltype 可以从一个表辑ּ?#8220;俘获”其结果的cdq?#8220;q回”Q?br /> l一的初始化语法
q有{号形式的:
对于 POD 集合Q又可以用大括号Q?br />
最后还有构造函数的成员初始化:
q么多初始化形式Q不仅菜鸟会搞得很头大,高手也吃不消。更惨的?C++03 中居然不能初始化 POD 数组的类成员Q也不能在?new[] 的时候初?POD 数组Q操蛋啊QC++11 q大括号一l天下了Q?br />
q有一大好事就是对于容器来_l于可以摆脱 push_back() 调用了,C++11中可以直观地初始化容器了Q?br />
而类中的数据成员初始化也得到了支持:
deleted 函数?defaulted 函数
叫做 defaulted 函数Q?default; 指示~译器生成该函数的默认实现。这有两个好处:一是让E序员轻松了Q少敲键盘,二是有更好的性能?br /> ?defaulted 函数相对的就?deleted 函数Q?br />
q货有一大用途就是实?noncopyabe 防止对象拯Q要想禁止拷贝,?=deleted 声明一下两个关键的成员函数可以了Q?br /> nullptr
所有跟指针有关的地斚w可以?nullptrQ包括函数指针和成员指针Q?br /> 委托构造函?/h2>
#2 是所谓的委托构造函敎ͼ调用了真正的构造函?#1?叛_引?/h2>
q种Ҏ(gu)很傻很天真,很慢Q因为需要申请内存,然后拯字符Q?move 只需要交换两个数据成员,无须甌、释攑ֆ存和拯字符数组Q?br />
要实现支?move 的类Q需要声?move 构造函数和 move 赋值操作符Q如下:
C++11 的标准库q泛使用 move 语义Q很多算法和容器都已l?move 语义优化q了?C++11 的标准库
U程?/h3>
新的指针c?/h3>
新的法
q有一个新?copy_nQ?br />
iota() 法可以用来创徏递增序列Q它先把初D值给 *firstQ然后用前置 ++ 操作W增长初值ƈ赋值到l下一个P代器指向的元素,如下Q?br />
是的QC++11 仍然~少一些很有用的库?XML APIQsocketQGUI、反?#8212;—以及自动垃圾攉。然而现有特性已l让 C++ 更安全、高效(是的Q效率更高了Q可以参?Google ? 基准试l果http://www.itproportal.com/2011/06/07/googles-rates-c-most- complex-highest-performing-language/Q以及更加易于学?fn)和使用?br /> 如果觉得 C++ 变化太大了,不必惊恐Q花Ҏ(gu)间来学习(fn)好了。可能在你融会诏通新Ҏ(gu)以后,你会同意 Stroustrup 的观点:C++11 是一门新的语a——一个更好的 C++?p>
]]>
许多Ch对于~译B(ti)OOST感到无从下手Q甚臛_此而放弃用BOOSTQ那真的太可惜了Q下面我把一些常用的BOOST~译Ҏ(gu)贴于此,同时也作qW记?
首先下蝲bjam.exeQ复制到 $BOOST$ 目录下。或?span style="FONT-SIZE: 14pt">自己生成bjamQ打开Visual Studio 2008 命o提示H口$BOOST$\tools\jam\srcQ执?build.bat 会在$BOOST$\tools\jam\src\bin.ntx86 生成 bjam.exe 文g。复制文?bjam.exe 文g?$BOOST$\?/span>?
1.完全~译安装Q?
bjam --toolset=msvc install
完成后会生成一个bin.v2~译时的临时目录Q手动删除。生成另一个目录C:\boostQ里面ؓ所有的头文件和库文件。头文g目录为boost_1_34_1\boost目录复制q去的?br>
2.只编译相应的库文?
bjam --toolset=msvc stage
完成后同样会生成bin.v2临时目录。另一个目录ؓstage文gQ里面有对应的库文g?
3.查看需要编译才能用的库列?
bjam --show-libraries
4.~译特定的库Q如只编译regex
bjam --toolset=msvc --with-regex stage
生成的库文g在stage目录中?
5.不编译某个库Q如不编译regex
bjam --toolset=msvc --without-regex stage
生成的库文g在stage目录中?
6.~译特定的库Q如只编译regexQ生成debugQ多U程Q共享连接版本,q保存在stage?
bjam --toolset=msvc --with-regex stage debug threading=multi link=shared
7.生成 mt-sgd 的静态库(runtime-link-static)
bjam "-sTOOLS=vc-8_0" --with-thread install debug release runtime-link=static
8.~译regex库?
bjam --toolset=msvc --with-regex stage debug release threading=multi threading=single link=shared link=static runtime-link=shared runtime-link=static
boost的安装方法:
对于DLL版本
bjam --toolset=msvc link=shared runtime-link=shared threading=multi stage debug release install
对于lib版本
bjam --toolset=msvc link=static runtime-link=shared threading=multi stage debug release install
另外Q在$BOOST$\tools\build\v2\user-config.jam扑ֈ下面的地?
# -------------------
# MSVC configuration.
# -------------------
# Configure msvc (default version, searched for in standard locations and PATH).
# using msvc ;
# Configure specific msvc version (searched for in standard locations and PATH).
# using msvc : 8.0 ;
#在这里添?vs2008 的配|?
using msvc : 9.0 : : /wd4819 /D_CRT_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_DEPRECATE /D_SECURE_SCL=0 ;
#在这里添?vs2005 的配|?
using msvc : 8.0 : : <compileflags>/wd4819 <compileflags>/D_CRT_SECURE_NO_DEPRECATE <compileflags>/D_SCL_SECURE_NO_DEPRECATE <compileflags>/D_SECURE_SCL=0 ;
然后q入 $BOOST$ 目录Q执行bjam.exe ~译命o
//下面的命令的各选项的说明:
//prefix boost安装到的路径Q生成的头文件和库文仉会放到该路径中)?
//重定义以下变量(利用-s讄Q:
//VC80_ROOT vc2005的安装\径,如果未将vc2005安装到默认位|,你必L定该V?
//TOOLS 使用的编译工Pvc2005对应的是vc-8_0
//PYTHON_ROOT ython的安装目录,如果未将BOOST安装到默认位|,你必L定该V?
//BUILD ~译l果选项Q默认会生成可能多的版本,如调试版Q发行版Q静态库Q动态库Q单U程Q多U程?
]]>
作者:孟岩
--------------------------------------------------------------------------------------
q是那篇C++0X的正文。太长,先写上半部分发了?/p>
对象范式则反映了力_分工之后的团队协作的工作特点——每个h各有所长,各司其职Q有各自的私有资源,工g和信息在Z之间彼此传递,最后完成工作。因此,对象范式也就形成了自己对E序的看法——程序是׃l对象组成,q些对象各有所能,通过消息传递实现协作?
对象之间互相发送消息,协作完成dQ?
{
private:
int *const ptr;
public:
A():ptr(NULL)
{
}
};
A one;
A two;
two = one;
唉,当时知道是这个原因后真的是羞涩难当,自己明知道const不能被赋值是常识问题Q但当时却死zL能找出原因?br>而更要h命的是我之前?a href="http://www.shnenglu.com/zhaoyg/archive/2009/10/07/98041.html">{偶尔学习(fn)C++标准} ?[对象的复?- 复制构造与赋值操作符] 中就已经对赋值操作符做了一些笔讎ͼ但我竟然把一些内容给忘了Q也可能是压根就没注意)?br>
现在重新p动合成的赋值操作符何时不能使用再单独做一ơ笔记?br>
当类中拥有以下一U或几种情况Ӟ隐式定义的赋值操作符是不能用的Q?br>1.非静态的const成员变量
2.非静态的引用cd成员变量
3.非静态的cȝ型的成员变量的赋值操作符不可讉K
4.Q如果有Q基cM的赋值操作符不可讉K
注,对于拥有以上情况中的一U或多种的类Q标准中的用词是Qill-formed。我不知道ؓ什么要用ill-formedQ字面意思:不规范)Q但不管怎样Q对于ill-formedQ编译器的行为就是,无法~译通过?br>
后记Q希望这ơ不要再忘记q些内容了?
]]>
如图Q?br>
当我看到上面q样的代码时Q我便认个代码无法编译通过的,因ؓ按我所知道的,函数fun所q回的是一个(f)时对象,而(f)时对象是不能被修改的Q然而fun()=T()语句便恰恰是在对临时对象q行修改。但是当我编译之后却?c)了,居然通过了,我很U闷?br>后来在网友的帮助下,在标准中扑ֈ了答案:
3.10.5
The result of calling a function that does not return a reference is an rvalue. User defined operators are functions, and whether such operators expect or yield lvalues is determined by their parameter and return types.
13.5.7
The identities among certain predefined operators applied to basic types (for example, ++a ≡ a+=1) need not hold for operator functions. Some predefined operators, such as +=, require an operand to be an lvalue when applied to basic types; this is not required by operator functions.
3.10
An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]
也就是说Q在对内|类型进行赋值操作时Q将调用内置的赋值操作符Q而这U内|的要求左操作数必须是左|而当对类cd对象q行赋值时Q所调用的是重蝲的赋值操作符Q但重蝲的操作符q没有要求必M用左|也就是说Q赋值操作符的左操作数可以是叛_{?br>后来得知Q在C++中右值可以是一个对象,?#8220;对象”指的是“一D内存存贮区?#8221;Q但C中的叛_则不是一个对象,他只是一个倹{?br>
以上内容如有不对之处Q还望不惜指正?br>
对lvalue和rvalue的较详细介绍L(fng)文章<Lvalues and Rvalues>Q?a href="http://www.shnenglu.com/zhaoyg/archive/2010/02/06/107405.html">http://www.shnenglu.com/zhaoyg/archive/2010/02/06/107405.html
Most books on C or C++ do not explain lvalues and rvalues very well. (I looked in a dozen books and couldn't find one explanation I liked.) This may be due to of the lack of a consistent definition even among the language standards. The 1999 C Standard defines lvalue differently from the 1989 C Standard, and each of those definitions is different from the one in the C++ Standard. And none of the standards is clear.
Given the disparity in the definitions for lvalue and rvalue among the language standards, I'm not prepared to offer precise definitions. However, I can explain the underlying concepts common to the standards.
As is often the case with discussions of esoteric language concepts, it's reasonable for you to ask why you should care. Admittedly, if you program only in C, you can get by without understanding what lvalues and rvalues really are. Many programmers do. But understanding lvalues and rvalues provides valuable insights into the behavior of built-in operators and the code compilers generate to execute those operators. If you program in C++, understanding the built-in operators is essential background for writing well-behaved overloaded operators.
Basic concepts
--------------------------------------------------------------------------------
Kernighan and Ritchie coined the term lvalue to distinguish certain expressions from others. In The C Programming Language (Prentice-Hall, 1988), they wrote "An object is a manipulatable region of storage; an lvalue is an expression referring to an object....The name 'lvalue' comes from the assignment expression E1 = E2 in which the left operand E1 must be an lvalue expression."
In other words, the left and right operands of an assignment expression are themselves expressions. For the assignment to be valid, the left operand must refer to an object-it must be an lvalue. The right operand can be any expression. It need not be an lvalue. For example:
int n;
declares n as an object of type int. When you use n in an assignment expression such as:
n = 3;
n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue.
Suppose you switch the left and right operands around:
3 = n;
Unless you're a former Fortran programmer, this is obviously a silly thing to do. The assignment is trying to change the value of an integer constant. Fortunately, C and C++ compilers reject it as an error. The basis for the rejection is that, although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. It doesn't refer to an object; it just represents a value.
I don't know where the term rvalue comes from. Neither edition of the C Standard uses it, other than in a footnote stating "What is sometimes called 'rvalue' is in this standard described as the 'value of an expression.'"
The C++ Standard does use the term rvalue, defining it indirectly with this sentence: "Every expression is either an lvalue or an rvalue." So an rvalue is any expression that is not an lvalue.
Numeric literals, such as 3 and 3.14159, are rvalues. So are character literals, such as 'a'. An identifier that refers to an object is an lvalue, but an identifier that names an enumeration constant is an rvalue. For example:
enum color { red, green, blue };
color c;
...
c = green; // ok
blue = green; // error
The second assignment is an error because blue is an rvalue.
Although you can't use an rvalue as an lvalue, you can use an lvalue as an rvalue. For example, given:
int m, n;
you can assign the value in n to the object designated by m using:
m = n;
This assignment uses the lvalue expression n as an rvalue. Strictly speaking, a compiler performs what the C++ Standard calls an lvalue-to-rvalue conversion to obtain the value stored in the object to which n refers.
Lvalues in other expressions
-------------------------------------------------------------------------------
Although lvalues and rvalues got their names from their roles in assignment expressions, the concepts apply in all expressions, even those involving other built-in operators.
For example, both operands of the built-in binary operator + must be expressions. Obviously, those expressions must have suitable types. After conversions, both expressions must have the same arithmetic type, or one expression must have a pointer type and the other must have an integer type. But either operand can be either an lvalue or an rvalue. Thus, both x + 2 and 2 + x are valid expressions.
Although the operands of a binary + operator may be lvalues, the result is always an rvalue. For example, given integer objects m and n:
m + 1 = n;
is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to:
(m + 1) = n; // error
which is an error because m + 1 is an rvalue.
As another example, the unary & (address-of) operator requires an lvalue as its operand. That is, &n is a valid expression only if n is an lvalue. Thus, an expression such as &3 is an error. Again, 3 does not refer to an object, so it's not addressable.
Although the unary & requires an lvalue as its operand, it's result is an rvalue. For example:
int n, *p;
...
p = &n; // ok
&n = p; // error: &n is an rvalue
In contrast to unary &, unary * produces an lvalue as its result. A non-null pointer p always points to an object, so *p is an lvalue. For example:
int a[N];
int *p = a;
...
*p = 3; // ok
Although the result is an lvalue, the operand can be an rvalue, as in:
*(p + 1) = 4; // ok
Data storage for rvalues
--------------------------------------------------------------------------------
Conceptually, an rvalue is just a value; it doesn't refer to an object. In practice, it's not that an rvalue can't refer to an object. It's just that an rvalue doesn't necessarily refer to an object. Therefore, both C and C++ insist that you program as if rvalues don't refer to objects.
The assumption that rvalues do not refer to objects gives C and C++ compilers considerable freedom in generating code for rvalue expressions. Consider an assignment such as:
n = 1;
where n is an int. A compiler might generate named data storage initialized with the value 1, as if 1 were an lvalue. It would then generate code to copy from that initialized storage to the storage allocated for n. In assembly language, this might look like:
one: .word 1
...
mov (one), n
Many machines provide instructions with immediate operand addressing, in which the source operand can be part of the instruction rather than separate data. In assembly, this might look like:
mov #1, n
In this case, the rvalue 1 never appears as an object in the data space. Rather, it appears as part of an instruction in the code space.
On some machines, the fastest way to put the value 1 into an object is to clear it and then increment it, as in:
clr n
inc n
Clearing the object sets it to zero. Incrementing adds one. Yet data representing the values 0 and 1 appear nowhere in the object code.
More to come
--------------------------------------------------------------------------------
Although it's true that rvalues in C do not refer to objects, it's not so in C++. In C++, rvalues of a class type do refer to objects, but they still aren't lvalues. Thus, everything I've said thus far about rvalues is true as long as we're not dealing with rvalues of a class type.
Although lvalues do designate objects, not all lvalues can appear as the left operand of an assignment. I'll pick up with this in my next column.
如下Q?/p>
--------------------------------------------------------------------------------
Non-modifiable Lvalues
--------------------------------------------------------------------------------
Lvalues actually come in a variety of flavors. If you really want to understand how compilers evaluate expressions, you'd better develop a taste.
const 限定W的含义Q?比如 int const m;
它ƈ不是说m的g能被修改, 而是?m 不能修改它引用的对象Q?br>e.g:
int m;
int const *p = &m;
m += 1; //right
*p += 1; //wrong
An expression is a sequence of operators and operands that specifies a computation. That computation might produce a resulting value and it might generate side effects. An assignment expression has the form:
e1 = e2
where e1 and e2 are themselves expressions. The right operand e2 can be any expression, but the left operand e1 must be an lvalue expression. That is, it must be an expression that refers to an object. As I explained last month ("Lvalues and Rvalues," June 2001, p. 70), the "l" in lvalue stands for "left," as in "the left side of an assignment expression." For example:
int n;
declares n as an object of type int. When you use n in an assignment expression such as:
n = 3;
the n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue. On the other hand:
3 = n;
causes a compilation error, and well it should, because it's trying to change the value of an integer constant. Although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. An rvalue is simply any expression that is not an lvalue. It doesn't refer to an object; it just represents a value.
Although lvalue gets its name from the kind of expression that must appear to the left of an assignment operator, that's not really how Kernighan and Ritchie defined it. In the first edition of The C Programming Language (Prentice-Hall, 1978), they defined an lvalue as "an expression referring to an object." At that time, the set of expressions referring to objects was exactly the same as the set of expressions eligible to appear to the left of an assignment operator. But that was before the const qualifier became part of C and C++.
The const qualifier renders the basic notion of lvalues inadequate to describe the semantics of expressions. We need to be able to distinguish between different kinds of lvalues. And that's what I'm about to show you how to do. But first, let me recap.
A few key points
--------------------------------------------------------------------------------
The assignment operator is not the only operator that requires an lvalue as an operand. The unary & (address-of) operator requires an lvalue as its sole operand. That is, &n is a valid expression only if n is an lvalue. Thus, an expression such as &3 is an error. The literal 3 does not refer to an object, so it's not addressable.
Not only is every operand either an lvalue or an rvalue, but every operator yields either an lvalue or an rvalue as its result. For example, the binary + operator yields an rvalue. Given integer objects m and n:
m + 1 = n;
is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to:
(m + 1) = n; // error
which is an error because m + 1 is an rvalue.
An operator may require an lvalue operand, yet yield an rvalue result. The unary & is one such operator. For example:
int n, *p;
...
p = &n; // ok
&n = p; // error: &n is an rvalue
On the other hand, an operator may accept an rvalue operand, yet yield an lvalue result, as is the case with the unary * operator. A valid, non-null pointer p always points to an object, so *p is an lvalue. For example:
int a[N];
int *p = a;
...
*p = 3; // ok
Although the result is an lvalue, the operand can be an rvalue, as in:
*(p + 1) = 4; // ok
With this in mind, let's look at how the const qualifier complicates the notion of lvalues.
Lvalues and the const qualifier
--------------------------------------------------------------------------------
A const qualifier appearing in a declaration modifies the type in that declaration, or some portion thereof. For example: int const n = 127;
declares n as object of type "const int." The expression n refers to an object, almost as if const weren't there, except that n refers to an object the program can't modify. For example, an assignment such as:
n = 0; // error, can't modify n
produces a compile-time error, as does:
++n; // error, can't modify n
(I covered the const qualifier in depth in several of my earlier columns. See "Placing const in Declarations," June 1998, p. 19 or "const T vs. T const," February 1999, p. 13, among others.) How is an expression referring to a const object such as n any different from an rvalue? After all, if you rewrite each of the previous two expressions with an integer literal in place of n, as in:
7 = 0; // error, can't modify literal ++7; // error, can't modify literal
they're both still errors. You can't modify n any more than you can an rvalue, so why not just say n is an rvalue, too? The difference is that you can take the address of a const object, but you can't take the address of an integer literal. For example:
int const *p;
...
p = &n; // ok
p = &7; // error
Notice that p declared just above must be a "pointer to const int." If you omitted const from the pointer type, as in:
int *p;
then the assignment:
p = &n; // error, invalid conversion
would be an error. When you take the address of a const int object, you get a value of type "pointer to const int," which you cannot convert to "pointer to int" unless you use a cast, as in:
p = (int *)&n; // (barely) ok
Although the cast makes the compiler stop complaining about the conversion, it's still a hazardous thing to do. (See "What const Really Means," August 1998, p. 11.)
Thus, an expression that refers to a const object is indeed an lvalue, not an rvalue. However, it's a special kind of lvalue called a non-modifiable lvalue-an lvalue that you can't use to modify the object to which it refers. This is in contrast to a modifiable lvalue, which you can use to modify the object to which it refers.
Once you factor in the const qualifier, it's no longer accurate to say that the left operand of an assignment must be an lvalue. Rather, it must be a non-modifiable lvalue. In fact, every arithmetic assignment operator, such as += and *=, requires a modifiable lvalue as its left operand. For all scalar types:
x += y; // arithmetic assignment
is equivalent to:
x = x + y; // assignment
except that it evaluates x only once. Since the x in this assignment must be a modifiable lvalue, it must also be a modifiable lvalue in the arithmetic assignment. Not every operator that requires an lvalue operand requires a modifiable lvalue. The unary & operator accepts either a modifiable or a non-modifiable lvalue as its operand. For example, given:
int m;
int const n = 10;
&m is a valid expression returning a result of type "pointer to int," and &n is a valid expression returning a result of type "pointer to const int."
What it is that's really non-modifiable
--------------------------------------------------------------------------------
Earlier, I said a non-modifiable lvalue is an lvalue that you can't use to modify an object. Notice that I did not say a non-modifiable lvalue refers to an object that you can't modify-I said you can't use the lvalue to modify the object. The distinction is subtle but nonetheless important, as shown in the following example. Consider:
int n = 0;
int const *p;
...
p = &n;
At this point, p points to n, so *p and n are two different expressions referring to the same object. However, *p and n have different types. As I explained in an earlier column ("What const Really Means"), this assignment uses a qualification conversion to convert a value of type "pointer to int" into a value of type "pointer to const int." Expression n has type "(non-const) int." It is a modifiable lvalue. Thus, you can use n to modify the object it designates, as in:
n += 2;
On the other hand, p has type "pointer to const int," so *p has type "const int." Expression *p is a non-modifiable lvalue. You cannot use *p to modify the object n, as in:
*p += 2;
even though you can use expression n to do it. Such are the semantics of const in C and C++.
In summary
--------------------------------------------------------------------------------
Every expression in C and C++ is either an lvalue or an rvalue. An lvalue is an expression that designates (refers to) an object. Every lvalue is, in turn, either modifiable or non-modifiable. An rvalue is any expression that isn't an lvalue. Operationally, the difference among these kinds of expressions is this:
Dan Saks is a high school track coach and the president of Saks & Associates, a C/C++ training and consulting company. You can write to him at dsaks@wittenberg.edu.
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/SeeSeaBee/archive/2007/09/08/1777120.aspx
以上代码是在论坛中遇见的Q当时我q没有立d应上来这是什么缘故,思烦一阵后Q才弄明白?br>原来Q在Derive中没有重写f函数Q又因ؓ当派生类没有重写基类的虚函数Ӟzcd象对该虚函数的调用,会调用其基cM的版本,而Derive又是多承,于是在Derivel承时就不知道Base1中的虚函数表应该记录哪个版本的f函数Q是Base2Q还是Base3?br>因ؓDerive中已重定义g函数QBase1的虚函数表记录的是Derive::g?/p>
==============================================================================
问题2Q?br>代码来源<effective C++>2nd
来源Q弘?nbsp; http://www.bansun.com/bbs/thread-4880-1-5.html