条款18:让接口容易被正确使用,不易被误用
我们以日期函数为例来讨论
class Date
{
public:
Date(int month,int day,int year);
.....
};
这个类客户容易犯错:
Date d(30,3,1995); //应该是Date d(3,30,1995);
Date(2,30,1995) //2月没有30
为了预防这种错误 可以引入新类型
struct Month
{
explicit Month(int m):val(m){ }
int val;
};
struct Day{.......}; struct Year{ ....... };
class Date{
public:
Date(const Month& m,const Day& d,const Year& y);
........
};
Date d(30,3,1995); //错误
Date d(Day(30),Month(3),Year(1995)); //错误
Date d(Month(3),Day(30),Year(1995)); //正确
我们现在要做的就是限制其值了
class Month
{
public:
static MonthJan(){ return Month(1);}
static MonthFeb(){return Month(2);}
.......
private:
explicit Month(int m);
........
};
Date d(Mouth::Mar(),Day(30),Year(1995));
我们应该避免类型与内置类型不兼容,保持接口一致性比如STL容器的接口
任何接口要求客户必须记得做某些事情,是不正确的行为比如createInvestment()的返回值,我们可以这样
std::tr1::shared_ptr<Investment> createInvestment();
我们知道shared_ptr接受两个参数:一个是被管理的指针,一个是引用计数为0时候的删除器,下面这个例子不是有效的C++:
std::tr1::shared_ptr<Investment> pInv(0,getRidOfInvestment);
0不是指针,我们应该明确的转换:
std::tr1::shared_ptr<Investment> pInv(static_cast<Investment*>(0),getRidOfInvestment);
因此我们实现createInvestment使他返回一个shared_ptr,并带getRidOfInvestment函数为删除器:
std::tr1::shared_ptr<Investment> createInvestment()
{
std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0),getRidOfInvestment);
retVal = ...;
return retVal;
}
另外shared_ptr可以防止DLL问题,可以自动解除互斥锁(条款14)。
条款19: 设计class犹如设计type
当你设计一个新的class就是设计一个type,我们应该考虑的问题:
1.新type如何创建与销毁
2.对象的赋值与初始化的区别
3.新type的值传递意味着什么?copy 构造函数的值传递如何实现?
4.什么是新type的合法值?
5.有什么样的继承体系
6.支持什么样的类型转换
7.支持什么样的操作符
8.什么声明为private、protected、public
9.这个新类型有多一般化,是定义一个新的class呢还是定义一个新的class template呢?
条款20:宁以pass-by-reference-to-const替换pass-by-value
提到参数的传递问题,是传递值还是引用
一般的参数我们可以选择传递引用,这样做可以减小开销。如传值会导致构造函数、析构函数、copy构造函数的调用,所以开销很大
此外,我们可以传递引用不会导致对象的切割问题。
条款21:必须返回对象的时候不要返回引用
《C++ primer》中提到不要返回局部对象的引用,因为引用是对象的别名,对象在作用域结束的时候会被销毁。这样会导致引用指向一个被销毁的对象。
有人说可以这么做,但是情况会更糟糕,new一个对象存放在heap中,但是这样来我们不知道怎么去delete这个对象了
还有人说可以声明static,这样做就可以返回引用了,还以为是不错的想法。但是这是一种不安全的行为。以为static保存的是最近的值。如果我们拿了个对象的运算结果做比较还是会导致错误的发生。因为比较的结果会始终会真。
class R{
public:
R(int n = 0,int d = 1);
....
private:
int n,d;
friend const R operator*(const R& lhs,const R& rhs);
};
const R& operator*(const R& lhs,const R& rhs)
{
static R ret;
result = ........;
return ret;
}
bool operator==(const R& lhs,const R& rhs);
R a,b,c,d;
....
if((a*b)==(c*d))....
这里if的条件会始终保持真的。
所以我们应该返回对象,这个时候函数的返回类型应该是对象不是引用:
inline const R operator*(const R& lhs,const R& rhs)
{
return R(lhs.n*rhs.n,lhs.d*rhs.d);
}
条款22:将变量声明为private
个人觉得没有必要单独作为一个条款来做,声明为private可以保持客户访问的一致性
谈到封装性private具有很好的封装性,可是protected并不比public更具有封装性