程序设计中经常遇到的便是如何设计接口,让用户能够直观的、快速的了解并上手使用接口。让接口容易被正确使用,不易被误用便是本条款所强调的。
理想上,如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不应该通过编译;如果代码通过了编译,他的作为就该是客户所想要的。
比如这样一个表示时间的类:
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;
}