Effective C++:条款18:让接口容易被正确使用,不易被误用
(一)
看下面这个例子:
class Date { public: Date(int month, int day, int year); };
很有可能引起下面这两个错误:
(1)他们也许会以错误的次序传递参数,如:Date d(30, 3, 1995);
(2)他们可能传递一个无效的月份或天数,如:Date d(2, 30, 1995); 许多像这类客户端错误。
解决方法:简单的外覆(wrapper types)类型来区别天数、月份、和年份,然后于Date构造函数中使用这些类型:
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; }; class Date { public: Date(const Month& m, const Day& d, const Year& y); ... }; Date d(30, 3, 1998); //错误,不正确类型 Date d(Day(30), Month(3), Year(1998));//错误,不正确类型 Date d(Month(3), Day(30), Year(1998));//正确,正确类型
一旦正确的类型就定位,限定其值有时候是通情达理的,比较安全的做法是预先定义:
class Month { public: static Month Jan() {return Month(1);} static Month Feb() {return Month(2);} static Month Mar() {return Month(3);} ... static Month Dec() {return Month(12);} private: explicit Month(int m); //阻止生成新的月份 ... };
所以就可以像下面这样调用了!这样的话就不能用无效的数据调用了。
Date d(Month::Mar(); Day(3), Year(1998));
(二)
预防客户错误的另一个办法是:限制类型内什么事可做,什么事不能做。常见的限制是加上const。
另一个一般性准则是:“除非有好理由,否则应该尽量令你的types的行为与内置types一致”。一旦怀疑,就拿ints做范本。
避免与内置类型不兼容,真正的理由是为了提供行为一致的接口,stl容器的接口十分一致,使得它们很容易被使用。所以尽全力提供行为一致的接口!
(三)
Investment* createInvestment();
在前面说过把这个返回的指针放入智能指针中,为了防止资源泄漏!
但是如果客户忘记用智能指针的话,那怎么办!
解决方法:很多时候,较佳接口的设计原则是先发制人,就令factory函数返回一个智能指针:
tr1::shared_ptr<Investment> createInvestment();
这便实质上强迫客户将返回值存储于一个tr1::shared_ptr内,几乎消除了忘记删除底部Investment对象(当它不再被使用时)的可能性。
(四)
如果我们要实现createInvestment使他返回一个tr1::shared_ptr并夹带getRidOfInvestment函数作为删除器:
tr1::shared_ptr<Investment> createInvestment() { tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0), getRidOfInvestment); retVla = ...; //令retVal指向正确的对象。 return retVal; }
在上面的代码中,我们创建了一个初值为 null ,并且以getRidOfInvestment为删除器的tr1::shared_ptr。
当然了,如果被pInv管理的原始指针可以在建立pInv之前先确定下来,那么肯定“将原始指针传给pInv构造函数”会比“先将pInv初始化为null,再对他进行一次赋值操作”更好!
(五)
cross-Dll problem:对象在一个dll中被new,却在另一个dll内被delete。在许多平台上,这一类“跨dll的new/delete成对运用”会导致运行期错误。
解决办法:
shared_ptr没有这个问题,因为它缺省的删除器是来自“shared_ptr诞生所在的那个Dll”的delete。
例如假设Stock是派生自Investment的:
std::tr1::shared_ptr<Investment> createInvestment() { return std::tr1::shared_ptr<Investment>(new Stock); }
返回的那个tr1::shared_ptr可被传递给任何其他的DLLs,无需在意“cross-DLL problem”。因为这个指向Stock的tr1::shared_ptr会追踪记录“当Stock的引用次数变成0时该调用的那个DLL‘s delete”。所以用shared_ptr可以潜在的消除cross-Dll problem。
请记住:
(1)好的接口很容易被正确使用,不容易被误用.你应该在你的所有接口中努力达成这些性质.
(2) "促进正确使用"的办法包括接口的一致性,以及与内置类型的行为兼容.
(3)"阻止误用"的办法包括建立新类型、限制类型上的操作、束缚对象值以及消除客户的资源管理责任.
(2)tr1::shared_ptr支持定制型删除器.这可防范DLL问题,可被用来自动解除互斥锁等等.
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。