用户操作
[即时聊天] [发私信] [加为好友]
错误ID:fatalerror99
214861次访问,排名322好友0人,关注者4
还好
fatalerror99的文章
原创 24 篇
翻译 80 篇
转载 5 篇
评论 384 篇
fatalerror99的公告
Copyleft © 2005 - 2008 by fatalerror99 (iTePub's Nirvana)

本 BLog 所有文章除注明转载者外,均为本人原创或翻译,欢迎转载。转载时请保持文章完整并注明出处。

强烈推荐使用 Mozilla Firefox 浏览本 BLog。

MSN: fatalerror9999@hotmail.com

e-mail: kong_kong@163.com

最近评论
fatalerror99:楼上说得对,原来的翻译确实错了,而且 object 应该都是单数,不是复数。
jjy:"每一件适用于 B objects(对象)的事情也适用于 D objects(对象),因为每一个 D objects 都 is-a(是一个)D objects(对象);" 应该是"因为每一个 D objects 都 is-a(是一个)B objects(对象);""
jjy:"一旦 derived class destructor(派生类析构函数)运行,这个 object(对象)的 derived class data members(派生类数据成员)就呈现为未定义的值,所以 C++ 就将它们视为不再存在。"
应该是base class destructor运行时...
七星瓢企鹅:央视的新楼盖起来了,听说北京人照例给他起了个外号叫“大裤衩”
mopyman:严重鄙视此人
turingbook
简直是利欲熏心
文章分类
收藏
    相册
    Effective C++, 3rd Edition 插图
    数学公式
    Our Projects
    boost 文档汉化 boost_doc_translation(RSS)
    不服不行
    Bjarne Stroustrup
    Alexander A. Stepanov
    Andrei Alexandrescu
    Bruce Eckel
    Charles Petzold
    Chris Sells
    David R. Musser
    Dennis M. Ritchie
    Donald E. Knuth
    Herb Sutter
    James Gosling
    Nicolai M. Josuttis
    Scott Meyers
    Stanley B. Lippman
    侯捷
    荣耀
    精点 BLog
    水瓶水蓝
    水瓶水蓝 —— 晃荡在阴阳两界的魂儿
    (RSS)
    CityLife 的流水账(RSS)
    为艺术而技术(RSS)
    乱发当风(RSS)
    微起涟漪 —— basse(RSS)
    暗金色月亮的赫拉迪克宝盒(RSS)
    杏坛雨的博客(RSS)
    王晓渔:书中自有……(RSS)
    开发 BLog
    alai04 的专栏(RSS)
    C++ 的罗浮宫(RSS)
    Coofucoo's Blog--The Unadulterated Coofucoo(RSS)
    GreenCode's Blog(RSS)
    ilovevc 的专栏(RSS)
    lxwde 的专栏(RSS)
    oiramario(RSS)
    ralph623 的专栏(RSS)
    renco 的专栏(RSS)
    Scorpio Auding @ Blog++(RSS)
    SnowFalcon 的专栏(RSS)
    Stan Lippman's BLog(RSS)
    Sutter's (Online) Mill(RSS)
    切尔斯基(RSS)
    周星星 之 Blog(RSS)
    孟岩(RSS)
    开心就好的代码人生(RSS)
    心如止水 —— coofucoo 的专栏(RSS)
    方舟(RSS)
    歌谣在风中飘舞(RSS)
    空谷幽兰,心如皓月 —— 陈皓专栏(RSS)
    艺术编程(RSS)
    透明思考 - 1(RSS)
    透明思考 - 2(RSS)
    陈硕的 Blog(RSS)
    开发网站
    CSDN.NET
    artima devdloper: Best practices in enterprise software development
    Experts Exchange
    IBM DeveloperWorks
    IBM DeveloperWorks 中国(RSS)
    Programmers' Heaven
    The Artima Developer Community
    The Code Project
    卡卡社区
    开发语言与环境
    (CHEZ (CHEZ SCHEME))
    .NET Languages
    PHP: Hypertext Preprocessor
    Eclipse.org home
    Python Programming Language
    REBOL Technologies
    ActiveState
    D Programming Language
    Eclipse Plugins
    Eclipse Plusin Central
    Eiffel Software
    GCL - GNU Common Lisp
    GNU Compiler Collection (GCC)
    Groovy
    IronPython
    NetBeans IDE
    Perl
    Ruby on Rails
    Ruby Programming Language
    The Programming Language Lua
    坛子若干
    iTePub
    自由小店 —— iTePub 共建共享电子图书交互平台
    (RSS)
    ChinaJavaWorld.com 技术论坛
    ChinaUnix
    CSDN 技术社区 —— 这个不说大家也知道
    Huihoo - Open Source Community
    ITPUB 论坛
    卡卡社区
    网络书店
    Amazon.com
    China-Pub 网上书店
    joyo Amazon 卓越亚马逊
    第二书店
    有一杯咖啡叫做 Java
    Hibernate
    Java Technology
    JavaWorld
    jGuru
    Spring Framework
    The Apache Software Foundation
    TheServerSide.COM: Your Enterprise Java Community
    有一部经典叫做 C++
    Boost C++ Libraries
    C Programming and C++ Programming
    C/C++ Reference
    cplusplus.com
    Programming in C++, Rules and Recommendations
    The ADAPTIVE Communication Enviroment (ACE)
    The C Standards Committee (ISO C)
    The C++ Standard Committee (ISO C++)
    有一只企鹅叫做 Linux
    Debian
    Fedora Project
    Linux Journal
    Linux Online!
    Linux 伊甸园
    Linux.com
    Red Hat
    SUSE Linux Enterprise from Novell
    The Linux Foundation
    The Linux Kernel Archives
    Ubuntu
    有一种自由叫做开源
    OpenBSD
    CodeGuru
    FreeBSD
    FSF - The Free Software Foundation
    GNU
    Huihoo - Open Source Community
    Open Source Initiative (OSI)
    OpenSolaris
    SourceForge.net
    The Open Enterprise Foundation (OEF)
    专业出版机构
    Addison-Wesley
    APress
    Manning Publications Co.
    McGraw-Hill
    O'Reilly
    Prentice Hall PTR
    Wiley
    Wordware Publishing, Inc.
    Wrox
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    翻译 [翻译] Effective C++, 3rd Edition, Item 18: 使 interfaces(接口)易于正确使用,而难以错误使用收藏

    新一篇: [翻译] Effective C++, 3rd Edition, Item 19: 视 class design(类设计)为 type design(类型设计) | 旧一篇: [翻译] Effective C++, 3rd Edition, Item 17: 在 standalone statements(独立语句)中将 new 出来的 objects(对象)存入 smart pointers(智能指针)

     

    Item 18: 使 interfaces(接口)易于正确使用,而难以错误使用

    作者:Scott Meyers

    译者:fatalerror99 (iTePub's Nirvana)

    发布:http://blog.csdn.net/fatalerror99/

    C++ 被淹没于 interfaces(接口)中。function interfaces(函数接口)、class interfaces(类接口)、template interfaces(模板接口)。每一个 interface(接口)都是客户和你的代码交互的一种方法。如果你在和通情达理的人打交道,那些客户也想做好工作。他们想要正确使用你的 interfaces(接口)。在这种情况下,如果他们错误地使用了它,就说明你的 interface(接口)至少有部分是不完善的。在理想情况下,如果一个 interface(接口)的一种用法的尝试不符合客户的预期,代码将无法编译,反过来,如果代码可以编译,那么它做的就是客户想要的。

    开发易于正确使用,而难以错误使用的 interfaces(接口)要求你考虑客户可能造成的各种错误。例如,假设你正在为一个代表日期的 class 设计 constructor(构造函数):

    class Date {
    public:
      Date(int month, int day, int year);
      ...
    };

    乍一看,这个 interface(接口)似乎是合乎情理的(至少在美国),但是客户可能很容易地造成两种错误。首先,他们可能会以错误的顺序传递 parameters(参数):

    Date d(30, 3, 1995);              // Oops! Should be "3, 30" , not "30, 3"

    第二,他们可能传递一个非法的月或日的数字:

    Date d(3, 40, 1995);              // Oops! Should be "3, 30" , not "3, 40"

    (后面这个例子看上去好像很愚蠢,但是想想键盘上,4 就在 3 的旁边,这种 "off by one" 类型的错误并不罕见。)(上面这个例子原文有误,根据作者网站勘误更改——译者注。)

    很多客户错误都可以通过新类型的引入来预防。确实,type system(类型系统)是你阻止那些不合理的代码通过编译的主要支持者。在当前情况下,我们可以引入简单的 wrapper types(包装类型)来区别日,月和年,并将这些类型用于 Data constructor(构造函数)。

    struct Day {            struct Month {                struct Year {
      explicit Day(int d)     explicit Month(int m)         explicit Year(int y)
      :val(d) {}              :val(m) {}                    :val(y){}

      int val;                int val;                      int val;
    };                      };                            };

    class Date {
    public:
     Date(const Month& m, const Day& d, const Year& y);
     ...
    };
    Date d(30, 3, 1995);                      // error! wrong types

    Date d(Day(30), Month(3), Year(1995));    // error! wrong types

    Date d(Month(3), Day(30), Year(1995));    // okay, types are correct

    DayMonthYear 做成可以封装数据的羽翼丰满的 classes 比上面的简单地使用 structs 更好(参见 Item 22),但是即使是 structs 也足够证明明智的新类型引入在阻止 interface(接口)的错误使用方面能工作得非常出色。

    只要将正确的类型放在适当的位置,它往往能合理地限定那些类型的值。例如,月仅有 12 个合法值,所以 Month 类型应该反映这一点。做到这一点的一种方法是用一个 enum(枚举)来代表月,但是 enums(枚举)不像我们希望的那样是 type-safe(类型安全)的。例如,enums(枚举)能被作为 ints 使用(参见 Item 2)。一个更安全的解决方案是预先确定所有合法的 Months 的集合:

    class Month {
    public:
      static Month Jan() { return Month(1); }   // functions returning all valid
      static Month Feb() { return Month(2); }   // Month values; see below for
      ...                                       // why these are functions, not
      static Month Dec() { return Month(12); }  // objects

      ...                                       // other member functions

    private:
      explicit Month(int m);                    // prevent creation of new
                                                // Month values

      ...                                       // month-specific data
    };
    Date d(Month::Mar(), Day(30), Year(1995));

    如果用 functions 代替 objects 来代表特定月的主意让你感到奇怪,那可能是因为你忘了 non-local static objects(非局部静态对象)的初始化的可靠性是值得怀疑的。Item 4 能唤起你的记忆。

    防止可能的客户错误的另一个方法是限制对一个类型能够做的事情。施加限制的一个通常方法就是加上 const。例如,Item 3 阐释了使 operator* 的返回类型 const-qualifying(具备 const 资格)是如何能够防止客户对 user-defined types(用户定义类型)犯下这样的错误:

    if (a * b = c) ...                    // oops, meant to do a comparison!

    实际上,这仅仅是另一条使类型易于正确使用而难以错误使用的普遍方针的一种表现:除非你有很棒的理由,否则就让你的类型的行为与 built-in types(内建类型)保持一致。客户已经知道像 int 这样的类型有怎样的行为,所以你应该努力使你的类型的行为无论何时都同样合理。例如,如果 abints,给 a*b 赋值是非法的。所以除非有一个非常棒理由背离这种行为,否则,对你的类型来说这样做也应该是非法的。拿不定主意时,就像 ints 那样做。

    避免和 built-in types(内建类型)毫无理由的 incompatibilities(不兼容性)的真正原因是为了提供行为一致的 interfaces(接口)。很少有特性比 consistency(一致性)更有可能导出易于正确使用的 interfaces(接口),也很少有特性比 inconsistency(不一致性)更有可能导出令人郁闷的 interfaces(接口)。STL containers(STL 容器)的 interfaces(接口)在很大程度上(虽然并不完美)是一致的,而这使得它们相当易于使用。例如,每一种 STL containers(STL 容器)都有一个名为 size 的 member function(成员函数)可以知道这个 container(容器)中有多少 objects。与此对比的是 Java,在那里你对 arrays(数组)使用 length property(属性),对 Strings 使用 length method(方法),而对 Lists 却要使用 size method(方法),在 .NET 中,Arrays 有一个名为 Length 的 property(属性),而 ArrayLists 却有一个名为 Count 的 property(属性)。一些开发人员认为 integrated development environments (IDEs)(集成开发环境)能补偿这些琐细的 inconsistencies(不一致),但他们错了。inconsistency(不一致)在开发者工作中强加的精神负担是任何 IDE 都无法完全消除的。

    任何一个要求客户记住做某些事情的 interface(接口)都是倾向于错误使用的,因为客户可能忘记做那些事情。例如,Item 13 引入了一个 factory function(工厂函数),它返回一个指向动态分配的 Investment hierarchy(继承体系)中的 objects(对象)的指针。

    Investment* createInvestment();       // from Item 13; parameters omitted
                                          // for simplicity

    为了避免 resource leaks(资源泄漏),从 createInvestment 返回的指针最后必须被删除,但这就为至少两种类型的客户错误创造了机会:没有删除指针,或删除同一个指针一次以上。

    Item 13 展示了客户可以将 createInvestment 的返回值存入一个类似 auto_ptrtr1::shared_ptr 的 smart pointer(智能指针)的方式,从而将使用 delete 的职责交给 smart pointer(智能指针)。但是如果客户忘记使用 smart pointer(智能指针)呢?在很多情况下,一个更好的 interface(接口)策略会通过让 factory function(工厂函数)首先返回一个 smart pointer(智能指针)而预先解决问题:

    std::tr1::shared_ptr<Investment> createInvestment();

    这就从根本上强制客户将返回值存入一个 tr1::shared_ptr,几乎完全消除了当底层的 Investment object 不再使用的时候却忘记删除的可能性。

    实际上,返回一个 tr1::shared_ptr 使得一个 interface(接口)设计者预防许多其它的与资源泄漏释放相关的客户错误成为可能,因为,就像 Item 14 阐述的:当一个 smart pointer(智能指针)被创建的时候,tr1::shared_ptr 允许将一个 resource-release function(资源释放函数)——一个 "deleter" ——绑定到这个 smart pointer(智能指针)上。(auto_ptr 则没有这个能力。)

    假设从 createInvestment 得到一个 Investment* 指针的客户期望将这个指针传给一个名为 getRidOfInvestment 的函数,而不是对它使用 delete。这样一个 interface(接口)又为一种新的客户错误打开了大门,这就是客户可能使用了错误的 resource-destruction mechanism(资源析构机制)(也就是说,用了 delete 而不是 getRidOfInvestment)。createInvestment 的实现通过返回一个在其 deleter 上绑定了 getRidOfInvestmenttr1::shared_ptr 能够预防这样的问题。

    tr1::shared_ptr 提供了一个取得两个 arguments(实参)(要被管理的指针和当引用计数变为零时要调用的 deleter)的 constructor(构造函数)。这里展示了创建一个以 getRidOfInvestment 作为 deleter 的 null tr1::shared_ptr 的方法:

    std::tr1::shared_ptr<Investment>      // attempt to create a null
      pInv(0, getRidOfInvestment);        // shared_ptr with a custom deleter;
                                          // this won't compile

    唉呀,这不是合法的 C++。tr1::shared_ptr constructor(构造函数)坚决要求它的第一个参数是一个指针,而 0 不是一个指针,它是一个 int。当然,它 convertible(能转换)为一个指针,但那在当前情况下并不够好,tr1::shared_ptr 坚决要求一个真正的指针。用一个 cast(强制转型)解决个问题:

    std::tr1::shared_ptr<Investment>      // create a null shared_ptr with
      pInv(static_cast<Investment*>(0),   // getRidOfInvestment as its
             getRidOfInvestment);         // deleter; see Item 27 for info on
                                          // static_cast

    据此,实现返回一个以 getRidOfInvestment 作为其 deleter 的 tr1::shared_ptrcreateInvestment 的代码看起来就像这个样子:

    std::tr1::shared_ptr<Investment> createInvestment()
    {
      std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0),
                                              getRidOfInvestment);

      retVal = ... ;                         // make retVal point to the
                                             // correct object

      return retVal;
    }

    当然,如果将被 retVal 管理的 raw pointer(裸指针)可以在创建 retVal 时被确定,最好是将这个 raw pointer(裸指针)传给 retVal 的 constructor(构造函数),而不是将 retVal 初始化为 null 然后再赋值给它。至于方法上的细节,参考 Item 26。(此段原文有误,根据作者网站勘误更改——译者注。)

    tr1::shared_ptr 的一个特别好的特性是它自动 per-pointer(逐指针)地使用 deleter 以消除另一种潜在的客户错误——“cross-DLL 问题。”这个问题发生在这种情况下:一个 object 在一个 dynamically linked library (DLL)(动态链接库)中通过 new 被创建,在另一个不同的 DLL 中被删除。在许多平台上,这样的 cross-DLL new/delete 对会导致运行时错误。tr1::shared_ptr 避免了这个问题,因为它的缺省的 deleter 只将 delete 用于这个 tr1::shared_ptr 被创建的同一个 DLL 中。这就意味着,例如,如果 Stock 是一个继承自 Investment 的 class,而且 createInvestment 被实现如下,

    std::tr1::shared_ptr<Investment> createInvestment()
    {
      return std::tr1::shared_ptr<Investment>(new Stock);
    }

    返回的 tr1::shared_ptr 能在 DLLs 之间进行传递,而不必关心 cross-DLL 问题。指向这个 Stocktr1::shared_ptrs 将保持对“当这个 Stock 的引用计数变为零的时候,哪一个 DLLs 的 delete 应该被使用”的跟踪。

    这个 Item 不是关于 tr1::shared_ptr 的——而是关于使接口易于正确使用,而难以错误使用的——但 tr1::shared_ptr 正是这样一个消除某些客户错误的简单方法,它大体上值回了使用它的成本。最通用的 tr1::shared_ptr 实现来自于 Boost(参见 Item 55)。Boost 的 shared_ptr 的大小是一个 raw pointer(裸指针)的两倍,为簿记和 deleter-specific(deleter 专用)数据使用动态分配内存,当调用它的 deleter 时使用一个 virtual function(虚拟函数)来调用,在一个它认为是 multithreaded(多线程)的应用程序中,每当改变引用计数,会导致 thread synchronization(线程同步)开销。(你可以通过定义一个 preprocessor symbol(预处理符号)来使多线程支持失效。)在缺点方面,它比一个 raw pointer(裸指针)大,比一个 raw pointer(裸指针)慢,而且要使用辅助的动态内存。在许多应用程序中,这些附加的运行时开销并不显著,而对客户错误的减少却是每一个人都看得见的。

    Things to Remember

    • 好的 interfaces(接口)是易于正确使用,而难以错误使用的。你应该在你的所有 interfaces(接口)中为这个特性努力。
    • 使易于正确使用的方法包括在 interfaces(接口)和 behavioral compatibility(行为兼容性)上与 built-in types(内建类型)保持一致。
    • 预防错误的方法包括创建新的类型,限定类型的操作,约束 object 的值,以及消除客户的资源管理职责。
    • tr1::shared_ptr 支持自定义 deleter。这可以防止 cross-DLL 问题,能用于自动解锁互斥体(参见 Item 14)等。

    发表于 @ 2005年07月24日 22:23:00|评论(loading...)|编辑

    新一篇: [翻译] Effective C++, 3rd Edition, Item 19: 视 class design(类设计)为 type design(类型设计) | 旧一篇: [翻译] Effective C++, 3rd Edition, Item 17: 在 standalone statements(独立语句)中将 new 出来的 objects(对象)存入 smart pointers(智能指针)

    评论

    #fatalerror99 (iTePub's Nirvana) 发表于2005-09-11 22:59:00  IP:
    TrackBack来自《翻译:Effective C , 3rd Edition, Item 32: 确保 public inheritance 模拟 》

    Ping Back来自:blog.csdn.net
    #某精灵 发表于2005-07-25 14:35:00  IP: 61.186.252.*
    丧心病狂需要理由吗?
    哦也许是需要的...
    我的上帝
    #fatalerror99 (iTePub's Nirvana) 发表于2005-07-25 15:21:00  IP: 61.186.252.*
    “丧心病狂不需要理由……”
    这本身就是一条足以让人丧心病狂的理由了
    #Fire 发表于2005-07-25 09:27:00  IP: 61.186.252.*
    支持每一天!
    #fatalerror99 发表于2005-07-25 22:20:00  IP: 61.186.252.*
    to nirvana

    第一段确实有些词不达意,因为那是在我迷迷糊糊半醉半醒时的产物,现在已经重译,虽然还是不太好懂,但相对来说应该好多了。
    #nirvana 发表于2005-07-25 11:47:00  IP: 61.186.252.*
    支持。
    质量有点下降哈。第一段有点不够顺畅。继续努力。
    #fatalerror99 发表于2006-01-03 10:36:00  IP: 60.24.150.*
    Item 18 改定
    #fks0123 发表于2008-01-30 19:26:31  IP: 58.25.243.*
    感谢分享你的劳动成果!

    我觉得“假设从 createInvestment 得到一个 Investment* 指针的客户期望将这个指针传给一个名为 getRidOfInvestment 的函数,而不是对它使用 delete。”这句应该翻成“假设你期望客户:获得 Investment* 指针时,需要调用 createInvestment 函数;销毁时,应该将此指针传给一个名为 getRidOfInvestment 的函数而不是对它使用 delete。”

    我觉得我这样翻也不好,但想了半天也没想出更好的来,遣词造句也是大学问啊。 由此更体会到fatalerror99兄翻译之辛苦。佩服佩服!
    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © fatalerror99