如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码就不该通过编译,如果代码通过了编译, 它的作为就该是客户所想要的。 class Date { public: Date(int month, int day, int year); ... }; 第一,以错误的次序传递参数: Date(30, 3, 1998);//应该是“3,30”而不是“30,3” 第二,传递一个无效的月份或天数:(打岔一个键) Date(2, 30, 1998);//应该是“3,30”而不是“2, 30” 许多客户端错误可以通过导入新的类型而获得预防: 简单的外覆(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));//正确,正确类型 一旦正确的类型就定位,限定其值有时候是通情达理的:enums不具备我们希望的类型安全性;比较安全的做法是预先定义 有效的Months: 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函数返回一个只能指针: std::tr1::shared_ptr<Investment> createInvestment(); 如果设计者希望那些从createInvestment 取得的Investment*指针不是用delete而是一个名为getRidOfInvestment的函数: std::tr1::shared_ptr<Investment> createInvestment() { std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0), getRidOfInvestment()); retVal = ...; //令retval 指向正确对象 return retVal; } tr1::shared_ptr有一个特别的好的性质:它会自动使用它的“每个指针专属的删除器”,因而消除另一个潜在的客户错误: 所谓的“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”。 1.“促进正确使用”的办法包括接口一致性, 与内置类型的行为兼容。 2.“防止误用”的方法包括,建立新类型、限制类型上的操作、束缚对象值、以及消除客户的资源管理责任。 3.tr1::shared_ptr支持定制型删除器(custom deleter)。这可防范DLL问题,可被用来自动解除互斥锁(mutexes)等等
|
请发表评论