程序设计中经常遇到的便是如何设计接口,让用户能够直观的、快速的了解并上手使用接口。让接口容易被正确使用,不易被误用便是本条款所强调的。 理想上,如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不应该通过编译;如果代码通过了编译,他的作为就该是客户所想要的。
比如这样一个表示时间的类:
class Date { public: Date(int month, int day, int year); };
如果我们不对接口做一些“强制性“的约束,该接口就可能被误用:
Date date(2022, 2, 25); //传参顺序不对 Date date(31, -1, 0); //参数非法
虽然上述调用可以通过编译,但却可能与用户意图违背。为此我们可以采用类型系统:
class Month { public: explicit Month(int m) : month(m) { } private: int month; }; class Day { public: explicit Day(int d) : day(d) { } private: int day; }; class Year { public: explicit Year(int y) : year(y) { } private: int year; }; class Date { public: Date(const Month &m, const Day &d, const Year &y); };
这时,我们对用户使用该类的方式做了一些约束,比如:
Date date(2022, 2, 25); //报错,explicit禁止隐式类型转换 Date date(Day(25), Month(2), Year(2022));//报错,传参顺序不对 Date date(Month(2), Day(25), Year(2022));//正确
虽然我们对传参的顺序,方式做了一定的约束,但是还是避免不了传入非法值,比如:
Date date(Month(22), Day(25), Year(2022));
月份为22显然不合法。为此,我们可以预先定义所有有效的值(即告知用户,只能使用这几个值哦):
class Month { public: static Month Jan() { return Month(1); } static Month Feb() { return Month(2); } //... private: explicit Month(int m); };
再使用时,只能这样,这样总不会写错了吧?:
Date date(Month::Jan(), Day(25), Year(2022));
预防用户错误的另一个做法是,限制类型内什么事可做,什么事不可做。常见的限制是加上const。
比如:我们重载了*
并且返回值被const限定,当用户一不小心将==
写成=
时,程序可以告知用户,唉?你这里是不是要做比较?而不是赋值??
if (a * b = c) // ?
后面部分理解不是很透彻,再续。
关于shared_ptr
删除器的使用:
void del(int *p) { cout << "del" << endl; } void func() { shared_ptr<int> p(new int(3), del); cout << p << endl; shared_ptr<int> p1(new int(4), [](int *p) { cout << p << endl; cout << "lambda" << endl; }); cout << p1 << endl; }