问题聚焦:
    从这个条款开始,我们把注意力转移到软件设计和声明上来,具体的说就是,C++接口的设计和声明。
    所谓软件设计,就是以一般习惯的构想开始,演变成细节的实现,最终开发针对性的特殊接口。
    接口的设计和声明的一个最基本的准则是:让接口容易被正确使用,不容易被误用。
    引入新类型是预防接口被误用的常见手段之一。

欲开发一个好的接口,首先必须考虑客户可能做出什么样的错误。
来看下面这个例子
class Date
{
public:
Date(int month, int day, int year);
... ...
};
上面这个接口有什么问题呢?(如果你觉得没问题的话,那么就该像我一样老实地看下去。。)
它的客户很容易犯下至少两个错误:
  1. 他们也许会以错误的次序传递参数,如:Date d(30, 3, 1995);
  2. 他们可能传递一个无效的月份或天数,如:Date d(2, 30, 1995);
许多像这类客户端错误,可以通过引入新类型获得预防。
struct Day {
explicit Day(int d) : val(d) { }
int val;
};
struct Month{
explicit Month (int d) : val(d) { }
int val;
};
struct Year{
explicit Year (int d) : val(d) { }
int val;
}; class Date {
public:
Date(const Month& , const Day& d, const Year& y);
... ...
}; // 使用
Date d(30, 3, 1995); // error,类型错误
Date d(Day(30), Month(3), Year(1995)); // error,类型错误
Date d(Month(3), Day(30), Year(1995));
预防客户错误的另一个办法是,限制类型内什么事可做,什么事不能做。
常见的限制是为函数或者重载操作符的返回值加上const。

让types容易被正确使用,不容易被误用。
下面主要通过智能指针std::tr1::shared_ptr的用法来说明这个思想。
任何接口如果要求客户必须记得做某些事情,就是有着被不正确使用的倾向,因为客户可能会忘记做那件事情。
如下这个工厂函数(参数省略)
Inverstment* createInvestment();
错误倾向:没有删除指针,或删除同一个指针超过两次。
方案:在条款13中已经告诉我们如何将createInvestment的返回值存储与一个智能指针,以防止资源泄漏。
错误倾向:客户忘记使用智能指针。
方案:先发制人——较佳接口的设计原则之一,令工厂函数返回一个智能指针。
std::tr1::shared_ptr<Investment> createInvestment();
回到原始的返回Investment*指针的版本,这时,有的客户希望通过函数getRidOfIvestment来析构这个资源,而不是直接的delete。
错误倾向:企图使用错误的资源析构机制,用delete而不是getRidOfInvestment
方案:使用str1::shared_ptr提供的第二个参数:自定义的删除器
//创建一个null shared_ptr指针,并自带一个删除器
std::tr1::shared_ptr<Investment> pInv(0, getRidOfInvestment); // error!第一个参数应该是一个指针,而不是int
// 使用转换cast
std::tr1::shared_ptr<Investment> pInv( static_cast<Investment*>(0), getRidOfInvestment ); // 基于上面的定制的tr1::shared_ptr来解决上面的这个错误倾向
std::tr1::shared_ptr<Investment> createInvestment()
{
std::str1::shared_ptr<Investment> retVal(static_cast<Investment*>(0), getRidOfInvestment);
retVal = ... ; // 令retVal指向正确对象
return retVal;
}
错误倾向:cross_DLL problem,即对象在动态连接程序(DLL)中被new创建,却在另一个DLL内被delete销毁。
方案:tr1::shared_ptr没有这个问题,因为它的删除器是自带的(第二个参数默认指定的),所以不会被别的DLL销毁。
tr1::shared_ptr看起来在“降低客户错误”方面的功能很强大,但是其代价就是该指针比原始指针大且慢,而且使用辅助动态内存。
所以如何使用,视使用场景具体判断。

小结:
  • 好的接口很容易被正确使用,不容易被误用。应该在接口设计时努力打到这点。
  • 促进正确使用的办法包括接口的一致性,以及与内置类型的行为兼容
  • 阻止误用的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任。
  • tr1::shared_ptr支持定制删除器,这可防范DLL问题,可被用来自动解除互斥锁问题。(Effective C++(14) 在资源管理类中小心copying行为
参考资料:
《Effective C++ 3rd》

Effective C++(18) 让接口更容易被正确使用,不易被误用的更多相关文章

  1. Effective C++ -----条款18:让接口容易被正确使用,不易被误用

    好的接口很容易被正确使用,不容易被误用.你应该在你IDE所有接口中努力达成这些性质. “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容. “阻止误用"的办法包括建立新类型.限 ...

  2. EC笔记:第4部分:18、接口正确使用,不易被误用

    好的接口容易被正确使用,不易被误用 考虑以下函数: void func(int year,int month,int day){ //一些操作 } 这个函数看似合理,因为参数的名字已经暴露了它的用途. ...

  3. [Effective C++ --018]让接口容易被正确使用,不易被误用

    □第一节 什么是接口?什么是接口?百度百科的解释:两个不同系统(或子程序)交接并通过它彼此作用的部分.接口的概念贯穿于整个软件开发过程中,本文讨论的接口概念仅局限于以C++实现的class,funct ...

  4. 采用ADM2483磁隔离器让RS485接口更简单更安全

    采用ADM2483磁隔离器让RS485接口更简单更安全 摘要:本文介绍RS485的特点及应用,指出了普通RS485接口易损坏的问题,针对存在的问题介绍了以ADM2483为核心的磁隔离解决方案. 关键词 ...

  5. 编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程

    建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...

  6. 【转】编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程

    建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...

  7. 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用

    1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些“讲道理”的人员,这些客户尝试把工作做好,他们希望能 ...

  8. 使用ICallbackEventHandler接口更高效实现Ajax

    使用ICallbackEventHandler接口可以方便地高效地实现Ajax功能 1.处理页面需实现ICallbackEventHandler接口,此接口有两个方法 a.GetCallbackRes ...

  9. 理解 Delphi 的类(十) - 深入方法[18] - 在接口区声明的方法都相当于提前声明了

    //要点18: 如果函数在接口区定义了, 就无需用 forward 提前声明了 unit Unit1; interface uses   Windows, Messages, SysUtils, Va ...

随机推荐

  1. zoj2977Strange Billboard (国家压缩+罗列)

    Strange Billboard Time Limit: 2 Seconds Memory Limit: 65536 KB The marketing and public-relations de ...

  2. 初步swift该研究指出语言(基本数据类型)

    笔者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/28258805 转载请注明出处 假设认为文章对你有所帮助,请通过留言 ...

  3. Git常用命令(转)

    目前开发的新项目使用的版本控制工具基本用的都是Git,老项目用的还是Svn,网上Git资源也很多,多而杂.我整理了一份关于Git的学习资料,希望能帮助到正在学习Git的同学. 一. Git 命令初识 ...

  4. 平原绫香 Hirahara Ayaka-Jupiter

    我第一次听到平原绫香(Hirahara Ayaka)久石让在武道馆演唱会.她的歌声真的很震惊! 声音是如此的纯净,演唱会是如此的优雅.她着迷,只是如此美丽. 然后我去检查她的信息.发现Jupiter. ...

  5. ACM算法

      一.数论算法 1.求两数的最大公约数 2.求两数的最小公倍数 3.素数的求法 A.小范围内判断一个数是否为质数: B.判断longint范围内的数是否为素数(包含求50000以内的素数表): 二. ...

  6. OOP 创建对象的7种方式

    JavaScript OOP 创建对象的7种方式   我写JS代码,可以说一直都是面向过程的写法,除了一些用来封装数据的对象或者jQuery插件,可以说对原生对象了解的是少之又少.所以我拿着<J ...

  7. MVC 插件化框架支持原生MVC的Area和路由特性

    .NET MVC 插件化框架支持原生MVC的Area和路由特性 前面开放的源码只是简单的Plugin的实现,支持了插件的热插拔,最近晚上偶然想到,原生的MVC提供Areas和RouteAtrribut ...

  8. 使用myeclipse创建带注解的model实体类

    1.先新建JPA项目: 如果没有就点击左下角的Show All Wizards. 点两次Next后,点击Finish即可,中间不用任何操作 (点第二次Next后会出现连接到所在数据库,先不管)     ...

  9. SQL 把表中字段存储的逗号隔开内容转换成列表形式

    原文:[原创]SQL 把表中字段存储的逗号隔开内容转换成列表形式 我们日常开发中,不管是表设计问题抑或是其他什么原因,或多或少都会遇到一张表中有一个字段存储的内容是用逗号隔开的列表. 具体效果如下图: ...

  10. 【高德地图API】一句话搞定webmap(一)——轻地图组件

    原文:[高德地图API]一句话搞定webmap(一)——轻地图组件 摘要: 遥想当年,在APP中加入LBS元素相当困难:要刻苦学习java,要刻苦学习iOS开发,要刻苦学习javascript…… 而 ...