struct Day{ explicit Day(int d):val(d){ } int val;};同样的对Month和Year定义类似的结构体,这样看起来貌似可以解决问题了。
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
2)用户在传参的时候数据范围不对。怎样才能让一个变量合理的表示它应该有的取值范围呢。比如说这里只有12个Month。OK,可以这样来定义:
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 objects,因为非局部的静态变量的初始化顺序没法保证。 这样保证用的时候变量都已经得到初始化了 static Month Dec() { return Month(12); } ... // 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));就OK了。
3)尽量让你设计的类型跟built-in types保持一致。避免无端与built-in types不兼容,其实就是提供行为一致性的接口。
4)任何接口如果如果要求客户必须记得做某些是,就是有着"不正确使用"的倾向,因为客户可能会忘记做那件事。比如说item13中动态分配的时候,为了避免内存的泄露,我们将返回的指针交给一个只能指针来负责内存的释放。
std::tr1::shared_ptr<Investment> createInvestment();
但万一客户忘记了使用智能指针怎么办。这里比较好的设计原则就是先发制人,令其直接返回一个智能指针。这样就几乎完全消除了忘记释放资源的可能性了。
5)又如item14中所言,tr1::shard_ptr允许绑定一个资源释放函数,但这有可能导致"企图使用错误的资源析构函数"。比如说我们想使用的资源析构函数是getRidOfInvestment,而不是delete,为了避免错误,我们可以返回一个将getRidOfInvestment绑定为删除器的tr1::shared_ptr指针。这样我们就试图在CreateInvestment中创建一个null的tr1::shared_ptr并以getRidOfInvestment作为删除器。
像这样:std::tr1::shared_ptr<Investment> pInv(0, getRidOfInvestment);
但是这样是不能编译通过的,因为0是int型,不能转化为指针,就算可以转化,这样也不好。这里可以使用强制转化来实现。这样代码就变成了:
std::tr1::shared_ptr<Investment> createInvestment() { std::tr1::shared_ptr<Investment> pInv(static_cast<Investment*>(0),getRidOfInvestment); pInv= ...;//指向一个正确的对象; return pInv; }6)当然,如果被pInv管理的原始指针可以在建立pInv之前先确定下来,那么"将原始指针传给pInv构造函数"会比"先将pInv初始化为null再对它做一次赋值操作"要好,详见item26.