【Scott Meyers】C++5×5断想之五:我之C++拍案惊奇

Scientific and Engineering C++ More Effective C++ 作者推荐的书籍,原版资源。此书第一部分为FORTRAN程序员解释C++,然后应用template达到炉火纯青的步骤。 立即下载

原文地址:http://www.artima.com/cppsource/top_cpp_aha_moments.html。译文发表于《程序员》2007.3。 

作者介绍

Scott Meyers,C++顶级权威之一,为世界各地客户提供培训和咨询服务。发表有畅销的Effective C++系列图书(《Effective C++》、《More Effective C++》和《Effective STL》),设计了创新型的Effective C++ CD,Addison Wesley的Effective Software Development Series顾问编辑,The C++ Source (http://www.artima.com/cppsource/)咨询板块专家。布朗大学计算机科学博士,他的网站是www.aristeia.com。  

 

 

在本系列第五也是最后一篇文章里,我将选出让自己拍案惊奇、豁然开朗的五个时刻。

如果你从事某项工作的时间足够长,必然有几次在疑窦丛生的同时,忽感豁然开朗的经历(如果没有过,那你肯定是入错行了)。每当这些时刻来临的时候,我都惊得呆了,禁不住大口吸气;好像原来只见黑白二色,突然穿越了时空,来到一个五光十色的世界。最后我慢慢回过味来,面带微笑。如此时刻让人激动。疑窦烟消云散,洞明取代了它的位置。

这样的事情在1978年就出现过一次。经过长期的煎熬,有一天我突然明白了指针的工作原理——如果说软件学习之路上也有成年礼的话,那它就是吧。但我那时还在使用Pascal编程,因此不能将它列入这个和C++相关的名单。现在公开我的选择吧:

 

认识到C++中特殊成员函数可以声明为private[注释1],1988年。和很多朋友一样,那时我正在自学C++。某天,刚毕业的同事John Shewchuk跑到我的办公室问我,“如果得到一个不可拷贝对象?”在场的有好几个人,但都不知如何回答。我们知道,如果不定义拷贝构造函数和拷贝赋值操作符,那么编译器会自动加入,最后得到的对象是可拷贝的。若要阻止编译器自动生成,我们就必须手工定义,但这样一来,对象还是可拷贝的。就像Grinch[注释2]一样,我们个个迷惑不解,没有人能找到解决办法。

后来(可能是当天或第二天,我记不清了),John宣称自己有了解决办法:将拷贝构造函数声明为private就搞定了。现在看来,这个问题是多么简单啊!但当时对于我们来说,不蒂于发现了新大陆;这是我们对C++知识融会贯通的重要一步。三年后,我出《Effective C++》第一版时,将这个简单的发现供奉在一个独立的条款(不到一页,大概是这本书中最短的条款)里。再后来,我愈加意识到这个发现的重要性,因此在《Effective C++》的后面两版中都写了进去。1988年,我不觉得这种用private阻止编译器隐式生成函数的方法显而易见;现在是2006年,我还是这么认为。

 

理解Barton 和Nackman在单位分析(译者注:Dimensional Analysis。更学术化的叫法是量纲分析)法中提出的无类型模板参数(non-type template parameters)的用法,1995年。1988年5月,我在《IEEE Software》上读到Robert F. Cmelik和Narain H. Gehani合著的一篇文章——《Dimensional Analysis with C++》。他们提出了一种在物理单位(如长度、速度和时间等)的计算过程中检测单位错误的方法。比如,用长度除以时间,再将结果和一个速度量比较是正确的,但和加速度量(它由长度除以时间的平方得来)比较就错了。Cmelik和Gehani提出,可以将单位信息存储到对象中,然后在运行时进行错误检测。这种方法将使对象变大,而且耗费运行时间。我觉得应该有更好的办法,但折腾再三也没有结果,后来就不了了之。

John J. Barton和Lee R. Nackman在他们1994年出版的《Scientific and Engineering C++》(Addison-Wesley出版社)中提出了一个很好的单位问题解决方案。不过,虽然我当时也拿到了这本书,却没有注意到该项成果——老实说,这本书写得太糟糕了点,我开了个头就扔到一边。直到1995年,我通读了Barton和Nackman发表在《C++ Report》上的专栏文章,他们这次用通俗易懂的语言描述了自己的方案。结果给我留下了三方面深刻印象。第一,它涵盖了单位的所有可能组合,而不仅仅以命名为依据的组合,因为命名是不完全的。例如,我们将长度除以时间的结果命名为速度,还将压力除以长度的平方的结果命名为压强,但却没有给长度乘以时间的平方再除以角速度的立方的结果一个名份。至少我不知道。即便计算中产生了迄今为止还用不到的单位组合,B&N方案也会确保单位分析的正确性。

第二是B&N方案的运行时消耗:没有。对象没有变大,程序也没有变慢。因此可以说B&N方案是无本而万利[注释3] 。这才是我真正感兴趣的组合方式。

不过最让连连称奇的,还是他们对无类型模板参数(代表各种基本单位的指数式)及其上算术指令(计算结果单位类型)的使用 [注释4]。这样,他们不仅解决了多年前搞得我兴趣索然的实际问题,而且使用的还是一项C++特性(即无类型模板参数。它在那以前引发了我无穷的好奇心)。

直到今天,我还为Barton和Nackman的成果激动。原本打算将他们在《C++ Report》上的文章列入我的“C++历史上最重要文献”名单,但后来我发现它影响甚微——很少有人像我那样认为他们的成果具有重大意义。现在,我觉得自己有点可耻,因为我只顾自己满足,却没有将好东西与更多人分享。

 

理解Visitor模式的涵义,1996或1997年。命名恰当,是软件工程的一个基本原则。这儿就有一个例子,充分说明了糟糕的命名会多么折磨人。我没觉得Visitor模式的设计机理有什么特别问题,但就是一直弄不明白它的意义。我无法将支离破碎的认识融会贯通。直到后来有一天,我终于明白:Visitor模式和“访问”毫无关系。其实,它是一种体系设计方法——要求引入新的虚拟行为函数时不必改变原体系的结构。抓住这点后,我一下就理解了这个模式的含义。但其命名于我造成了巨大理解障碍,甚至看了《Design Patterns》(http://www.artima.com/cppsource/top_cpp_books.html#dp)如下的描述后:

Visitor使你不用改变行为操作的元素的类,就可以定义新行为。

这个解释清楚而直接,现在看来很好理解,但我当时就是盯住了模式的名字,总觉得“Visitor”应该和“访问”、“遍历”啥的发生点关系。

出现这种结果,我想有两个可能原因。一是我死心眼,见识短浅,鼠目寸光。再有就可能是这个名字选得过于随意。如果名字本身指的张三,而使用文档上说的却是李四,那么至少一些人——比较执拗的那种——肯定要搞糊涂了。我倾向于后一种解释。

 

理解“remove”为什么实际上并没有删除任何东西,1998年?我与STL的remove算法相遇得不是时候。当我期望Visitor设计模式访问个啥的时候,我也认定remove算法就应该删除某个东西。但结果让我非常震惊,我发现在容器上执行remove[注释5]时,容器内元素数目根本不会改变!我有一种被出卖的感觉——我是正儿八经要求删除啊!骗子!谎言!无聊的广告!

后来我读到一篇文章——可能是Andrew Koenig的《C++ Containers are Not Their Elements》(发表于《C++ Report》1998年11-12月刊)——它才让我明白STL内部的真相:算法不能改变容器内元素的数目,因为算法根本不知道容器的类型。容器还可能是一个数组呢,显然数组的大小是不可改变的[注释6]。自然,算法应该和容器彼此独立,互不影响。我认识到,“remove”不会改变容器内元素数目,因为它不能。直到那时,我才算真正理解了STL的内部结构,知道迭代器(iterator)虽然通常由容器成员函数提供,但就像容器和算法一样,其实它也是完全独立的实体。后来,我把这篇文章读了很多次。类似上面的解释,可能别人都说过很多回了,但别怪我鹦鹉学舌,这可是我第一次真正理解remove。

自此以后,我就能与remove和睦相处了。再后来,当发现remove不仅将份内事情做得很好,而且效率超过绝大多数程序员自己编写的循环(remove的运行时间是线性的,而普通循环是二次的)时,我甚至对它有点另眼相看了。虽然我仍然不太喜欢这个命名,但也说不清到底哪个名字既能准确描述其行为,又便于记忆。

 

理解Boost库里shared_ptr的deleter如何工作,2004年。Boost的引用计数智能指针shared_ptr很有趣——你可以向其构造器传递一个函数或者仿函数(function object,或functor),当引用计数归零的时候,它将在被引用对象上调用删除器(deleter)[译注7]。乍一看,似乎没啥了不起啊,但请看代码:

 

template

class shared_ptr {

public:

      template

      explicit shared_ptr(U* ptr, D deleter);

      ...

};

 

注意shared_ptr 必然在析构时调用类型为D的删除器,然而它根本不知道D为何物。这个对象不能包含类型为D的数据成员,也不能指向类型为D的对象,因为声明其数据成员时,D对它而言还是未知的。那么,shared_ptr对象如何跟踪删除器(它在构造阶段传入;当T对象将被销毁时,还得使用它)呢?更通俗地说,构造器如何将未知类型的信息传递给它正在构造的对象,而这个对象本身对信息类型完全无知?

答案很简单:让此对象包含一个指向已知类型基类的指针(Boost叫它sp_counted_base),然后让构造器以D为参数实例化一个派生于上述基类的模板(Boost中叫sp_counted_impl_p和sp_counted_impl_pd),最后用声明于基类、实现于派生类的虚函数(Boost中使用dispose)去调用删除器。用图表示更为直观:

 

 

完全明白了——只要你看过这个图[译注8、9]。而且,看过此图后,我想你马上就会意识到它可以应用在很多领域;它为模板设计拓宽了思路,比如,模板化类使用很少的模板参数(例如shared_ptr只有一个),就可以跟踪无限个先前未知类型的信息)。当我想到这些的时候,我禁不住面露赞许的微笑,难抑钦佩之情[译注10]。

 

好了,文章结束,这就是我的5×5系列的末篇。简单总结一下本系列全部文章:C++历史上最重要的图书、文献、软件、人物,最后是对我来说最难以忘怀的五个神奇时刻。我还将再次和大家讨论这类有趣的话题,不过那至少应该是又一个18年后了。

 

注释:

1. 可能不一定非叫“特殊”成员函数不可——不过《Standard》如此称呼——具体包括缺省构造器、拷贝构造器、拷贝赋值操作符和析构函数。之所以“特殊”,是因为如果使用了它们而又未显式声明,编译器一般会隐式生成。

2. “Grinch两腿冰冷站在雪地里,想了又想:‘怎么会这样呢?’”参见Dr. Seuss的《Grinch如何偷走圣诞节》(Random House出版社,1957年)。不过在网上也能看到哦(http://www.kraftmstr.com/christmas/books/grinch.html,别告诉Random House)。

3. 我指的是不消耗更多运行时时间。他们大量使用了模板,当然会增加编译时间。

4. 你将两个数相乘时,就等于增加了指数次数,没忘记吧?

5. 我指的是STL的通用算法,而非list类的成员函数。

6. 重新分配内存不能算,不是所有的数组都是动态分配的。

7. 不要吃惊,TR1的shared_ptr就是以Boost的shared_ptr为基础并提供了相同功能。我正讨论的是Boost的shared_ptr,因为它有一个实现,我们这里说的也是实现问题。TR1仅仅是一个规范,如果你书生意气,问我TR1里讲的东西如何实现,那就没意思了。

8. 更准确地说,你以前应该看到过相关解释。譬如我,我得到的解释(我经常要别人给我讲解C++方面的问题)来自于Usenet新闻组的一个免费讨论(http://tinyurl.com/r66ql)。

9.我相信它也是外部多态(External Polymorphism,http://www.cs.wustl.edu/~schmidt/PDF/External-Polymorphism.pdf)设计模式的一个应用。自从我读过Chris Cleeland 和Douglas C. Schmidt于1998年9月发表在《C++ Report》上的有关此模式的文章后,我就喜欢上它了。不过直到现在,我仍然没看到这个模式的广泛应用。

10. 我觉得有两点需要说明。第一,它是Boost中大量成功创新的一个典范,Boost的创新性正是我将其列入“最重要C++软件”的原因之一。第二,很可惜的是,Boost中的这些创新未被整理并在C++社区广泛传播,很多有趣的东西,都掩藏在Boost库的盖子下不为人知。




C++ for Computer Science and Engineering,4th Edition 简介:1 Introduction to Programming 2 Numerical Processing 3 Additional Processing Details 4 Decisions 5 Files and Loops 6 Writing Your Own Functions 7 More on Functions 8 Character Processing and Do Case 9 Arrays 10 Using Arrays 11 Strings 12 Multidimensional Arrays 13 Structures Appendix A: How to Use Microsoft’s Visual C++ .NET 2005 Compiler Appendix B: How to Use Microsoft’s Visual C++ .NET 2002 Compiler Appendix C: How to Use Microsoft’s Visual C++ Version 6.0 Compiler Index 立即下载

相关推荐

[Effective.C.中文版].(Scott.Meyers).(中文版&第3版).pdf

[Effective.C.中文版].(Scott.Meyers).(中文版&第3版).pdf 个人收集电子书,仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!

Scott Meyers 从5个方面回顾和反思 C++ 历史

Scott Meyers 在 C++ Source(http://www.artima.com/cppsource)上发表的5篇回顾C++历史的文章,分别回顾了迄今为止最重要的C++图书、C++文章、C++人物、C++软件和他个人的C++回忆,非常值得阅读:A Pause to Reflect: Five Lists of Five, Part IThe Most Important C++

方舟 K ][ N G of A R K 9156

SAP采购申请详述;以及采购计划协议/交货计划行

SAP采购申请详述;以及采购计划协议/交货计划行

saplakes的博客 4344

Scott Meyers:C++史上最重要的五本书籍和五篇技术文章

 这几天Scott Meyers在artima上发起了C++书籍的大讨论,他给我们列了他认为最牛的五本经典书籍,依次是:The C++ Programming Language by Bjarne Stroustrup, Addison-Wesley, 1986 (1st edition), 1991 (2nd edition), 1997 (3rd edition), 2000 (spe

1万+

Vercel静态站被墙别慌!手把手教你用Cloudflare免费CDN+域名解析搞定DNS污染

本文详细介绍了如何利用Cloudflare免费CDN和域名解析解决Vercel静态网站遭遇DNS污染的问题。通过诊断DNS污染、设计Cloudflare解决方案架构、实战配置步骤以及高级调优技巧,帮助用户快速恢复站点访问并提升防护能力。

weixin_27442001的博客 707

squidpy 教程 2:利用 Scanpy 和 Squidpy 进行空间数据的可视化分析

本教程详细介绍了如何利用Squidpy和Scanpy对空间转录组数据进行可视化分析。通过将Scanpy处理得到的聚类结果映射回原始组织图像,并结合基因表达与空间邻域信息,帮助研究者直观揭示细胞类型分布、基因表达模式及空间相互作用,从而深入挖掘空间组学数据的生物学意义。

weixin_29081703的博客 446

Scott Meyers给出的他认为C++史上最强的五篇文章

下面是Scott Meyers给出的他认为C++史上最强的五篇文章,最早的也要到97年了,反正除了Template Metaprograms有点印象其它是都没看过,打算抽时间把这些大牛推荐的Non-Book Publications再消化一下。 Programming in C++, Rules and Recommendations by Mats Henricson and Erik Ny

freerock 2734

STM32F4系列HAL库配置定时器实验——输入捕获

STM32F4系列HAL库配置定时器实验——输入捕获 输入捕获简单讲解 输入捕获模式可以用来测量脉冲宽度或者测量频率。我们以测量周期和频率为例,用一个简图来说明输入捕获的原理 假定定时器工作在向上计数模式, 图中 t1~t2 时间,就是我们需要测量的高电平时间。测量方法如下:首先设置定时器通道 x 为 上升沿捕获,这样,t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样,根据定时器

azs0504的博客 9288

【Scott Meyers】C++5×5断想之四:C++历史上最重要人物

原文地址:http://www.artima.com/cppsource/top_cpp_people.html。译文发表于《程序员》2007.2。 作者介绍Scott Meyers,C++顶级权威之一,为世界各地客户提供培训和咨询服务。发表有畅销的Effective C++系列图书(《Effective C++》、《More Effective C++》和《Effective STL》)

8506

人形机器人骨骼材料选型实战:从铝合金到碳纤维的5种方案对比

本文深入探讨人形机器人骨骼材料的选型策略,对比铝合金、镁合金、钛合金、碳纤维复合材料及3D打印材料等5种方案的性能优劣。通过建立强度-重量比、疲劳寿命等核心评价维度,结合具体应用场景,提供科学的材料决策方法论,助力开发者优化机器人结构设计,实现性能与成本的平衡。

weixin_29199873的博客 442

动态规划之【树形DP】第3课:树形DP应用案例实践2

动态规划之【树形DP】第3课:树形DP应用案例实践2

王老师青少年编程 771

转:【Scott Meyers】C++5×5断想之五:我之C++拍案惊奇

<br />转自:http://blog.youkuaiyun.com/kybd2006/archive/2007/09/20/1793463.aspx<br /> <br />【Scott Meyers】C++5×5断想之五:我之C++拍案惊奇 <br /><br />原文地址:http://www.artima.com/cppsource/top_cpp_aha_moments.html。译文发表于《程序员》2007.3。 作者介绍<br />Scott Meyers,C++顶级权威之一,为世界各地客户提供培训和咨

293

C#里用HttpListener实现WEB API接口服务端

本文介绍了一个基于WinForm的HTTP服务实现方案。该方案使用HttpListener类创建了一个支持JSON POST请求处理的轻量级Web服务器,主要特点包括:1) 纯线程方式实现请求监听和处理;2) 提供标准化的JSON响应格式;3) 支持自定义请求处理逻辑;4) 包含完整的日志记录功能。代码示例展示了如何初始化服务、处理不同类型的请求(包括JSON POST),并提供了线程安全的UI更新机制。该实现适用于需要简单HTTP接口的Windows桌面应用程序,可作为程序间通信的基础设施。

大坡3D软件开发 231

C++5×5断想之五:我之C++拍案惊奇

在本系列第五也是最后一篇文章里,我将选出让自己拍案惊奇、豁然开朗的五个时刻。 如果你从事某项工作的时间足够长,必然有几次在疑窦丛生的同时,忽感豁然开朗的经历(如果没有过,那你肯定是入错行了)。每当这些时刻来临的时候,我都惊得呆了,禁不住大口吸气;好像原来只见黑白二色,突然穿越了

程序猿 652

YOLO26优化:特征融合 | 一种新颖的多尺度特征融合iAFF,适配小目标检测

在YOLO26中如何使用:iAFF加入Neck替代Concat;

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 273

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

上一篇: Anders Hejlsberg 在微软Visual studio.NET 发布大会上的讲话
下一篇: 【Scott Meyers】C++5×5断想之一:C++历史上最重要的图书
techweb
博客等级 码龄19年 226粉丝 3842原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值