EC读书笔记系列之10:条款16、17
条款18 让接口容易被正确使用,不易被误用
记住:
★“促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容
★“阻止误用”的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任(即类的设计者应先发制人)。
★tr1::shared_ptr支持定制型删除器。这可防范DLL问题,可被用来自动解除互斥锁等等。
--------------------------------------------------------------------------
C++的接口包括:function接口、class接口、template接口......
接口的理想情况:若客户企图使用某个接口而却没有获得他所预期的行为,应让这个代码通不过编译;若代码通过了编译,它的作为就该是客户所想要的。
a 通过导入新类型来预防接口被误用:
1 class Date { 2 public: 3 Date( int month, int day, int year ); 4 ... 5 }; 6 客户易犯错误: 7 Date d( 30, 3, 1995 ); //传递参数次序错误 8 Date d( 2, 30, 1995 ); //传递一个无效的月份或天数
可以通过导入简单的外覆类型(即建立新类型)来解决:
struct Day { explicit Day( int d ):val(d) {} //结构体中也可以使用成员初始化列表, //涨姿势了!!! int val; }; struct Month { explicit Month( int m ):val(m) {} int val; }; struct Year { explicit Year( int y ):val(y) {} int val; }; Date class改为: class Date { public: Date( const Month &m, const Day &d, const Year &y ); ... }; 使用时: Date d( Month(3), Day(30), Year(1995) );
b 通过限制对象值来预防接口被误用:
如预先定义所有有效的Months: class Month { public: static Month Jan() { return Month(1); } //返回有效月份 static Month Feb() { return Month(2); } //同上 ... private: explicit Month( int m ); //阻止生成新的月份,这是月份专属数据 }; 使用时: Date d( Month::Mar(), Day(30), Year(1995) );
c 通过限制类型上的操作来预防接口被误用:
如:常见的限制是加上const。
d 消除客户的资源管理责任(即类的设计者应先发制人):
对于如下语句:
Investment* createInvestment(); //此函数返回的是dumb ptrs
客户可将其返回值置入智能指针,因而将delete责任推给智能指针,但万一客户忘记就完了,此时应先发制人,就令上面函数返回一个智能指针:
std::tr1::shared_ptr<Investment> createInvestment() { //建立一个null shared_ptr并以getRidOfInvestment为删除器 std::tr1::shared_ptr<Investment> retVal( static_cast<Investment*>(0), getRidOfInvestment ); retVal = ...; //令retVal指向正确对象 return retVal; }
shared_ptr有个好的性质是:它会自动使用它的“每个指针专属的删除器”,因而消除另一个潜在的客户错误:cross-DLL problem。这个问题发生于“对象在DLL中被new创建,却在另一个DLL内被delete销毁”。在许多平台上,这一类“跨DLL之new/delete成对运用”会导致运行期错误。shared_ptr无这个问题,∵它缺省的删除器是来自“tr1::shared_ptr诞生所在的那个DLL”的delete。
shared_ptr比原始指针大且慢,而且使用辅助动态内存。在许多应用程序中这些额外的执行成本并不显著,然而其“降低客户错误”的成效却相当显著(意指性价比很高)。
条款19 设计class犹如设计type
记住:
★class的设计就是type的设计。在定义新的type之前,请确定你已经考虑过本条款覆盖的所有讨论主题