条款18 让接口被正确使用,不易被误用
除非有好的理由,否则应该让你的types的行为与内置types一致,请拿ints做范本
提供行为一致的接口!
阻止误用的办法包括建立新类型,限制类型上的操作,束缚对象值(比如要统计年月日,限制月的大小在1-12),消除客户的资源管理责任(智能指针)
shared_ptr支持定制型删除器,可被用来自动解除互斥锁等
条款20 宁以pass-by-reference-to-const替换pass-by-value
值传递要调用copy构造函数,释放时要调用多次析构函数(有副本),费时
const的必要性:引用传递代替值传递时,确保不会对传入的数据做改变
防止被切割:
class Window{
public:
virtual void display();
}
class WindowWithScrollBars:public Window{
public:
virtual void display();
}
void show(Window w){
w.display();
}
当用一个WindowWithScrollBars对象传入show时,因为是值传递,会导致其特化信息被切割,变成了一个Window对象!无法多态了!
应该如下:传进来的窗口是什么类型,w就表现出哪种特性
void show(const Window& w){
...
}
说到底,引用是指针实现出来的,引用传递说到底也是传递的指针,如果有一个对象属于内置类型,值传递效率会比引用传递效率高一些。
值传递对于内置类型和STL的迭代器和函数对象来说代价不贵,其他类型还是选用const引用传递好!
条款21 必须返回对象时,别妄想返回reference
不是所有情况都是引用传递好
const A& operator*(const A& rhs){
A result(rhs);//调用构造函数
return result;
}
返回了一个result的引用,但result是一个局部变量,离开作用域就被析构了!!!
引用不能返回一个局部对象,否则一败涂地
一个必须返回新对象的函数的做法是:就让他返回一个新对象呗
const A operator*(const A& rhs){
A result(rhs);//调用构造函数
return A(rhs);
}
承受一个operator*构造和析构函数的代价即可
绝对不要返回一个指针或引用指向一个local stack对象(出作用域会被析构),或返回引用指向一个heap-allocated对象(无法保证合理的delete),或返回指针或引用指向一个local static对象而有可能同时需要多个这样的对象(一个指针修改了指向对象的参数后,其他指针指向的参数也被修改了)
条款22 将成员变量声明为private
语法一致性:成员变量不是public,用户只能通过public里的相应函数来访问成员变量,用户使用时就都有一致的使用规则(全都要使用小括号等)
使用函数可以对成员变量的处理有更精确的控制,如可以编写const函数实现只读访问,不加const实现读写访问等
封装性,防止成员变量被更改
假如有一个public成员变量,我们最终取消了它,所有使用它的代码都会被破坏,假如有一个protected成员变量,我们最终取消了它,所有使用它的派生类都会被破坏。因此protected其实并不比public更加具有封装性
说到底,选择private就好
条款23 以non-member non-friend替换member函数
能够访问private成员变量的函数只有class的member函数加上friend函数,如果要在一个member函数(不只可以访问private数据,也能取用private函数、enums、typedefs等)和一个non-member,non-friend函数做抉择,较好封装性的时后者。因为它并不增加能够访问class内private成分的函数数量
将所有便利函数放在多个头文件内但同属于一个命名空间,用户可以轻松添加这一组便利函数,即可以添加更多的non-member,non-friend函数到此命名空间
参考C++标准程序库,vector、algorithm等,导入头文件再进行调用,即可完成很多事情
条款24 若所有参数皆需要类型转换,请为此采用non-member函数
class Rational{
public:
Rational(int numerator=0,int denominator=1);
int numerator() const;
int denominator() const;
const Rational operator* (const Rational& rhs) const;
}
Rational onehalf(1,2);
Rational result=onehalf*2;//很好!
Rational result=2*onehalf;//不行!
原因在于:
result=onehalf.operator*(2);//发生了隐式转换 得益于之前没有将构造函数声明为explicit
result=2.operator*(onehalf);
2没有相应的class,没有operator*成员函数,当然无法执行
结论为:只有当参数被列于参数列内,这个参数才是隐式转换的合格参与者
改变做法为将operator*变成non-member函数,允许编译器在每个实参上执行隐式转换
operator*是否应该成为class的一个friend函数呢?否定的,因为operator*完全可以借用Rational的public接口完成任务,这告诉我们:member函数的反面是non-member,而不是friend
如果你需要为某个函数的所有参数(包括this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是non-member
条款25 考虑写出一个不抛出异常的swap函数
当做swap时,如果交换内部的指针,效率就高了呀
以指针指向一个对象,内含真正的数据,即pimpl手法(pointer to implementation)
class WidgetImpl{
public:
...
private:
int a,b,c;
}
class Widget{
public:
void swap(Wideget& other){
using std::swap;//必要的,在找不到class里的swap函数调用此函数
swap(p,other.p);
}
private:
WidgetImpl* p;
}
//修订后的std::swap特化版本
namespace std{
template<>
void swap<Widget>(Widget& a,Widget& b)
{
a.swap(b);//调用a的swap成员函数
}
}
这种方法和STL有一致性,因为STL也提供有public的成员函数和std::swap的特化版本
如果swap的默认版本的效率你可以接受,那不需要做任何事
如果swap的默认版本实现效率不足:1、提供一个public swap成员函数,让它高效的置换两个对象(pimpl) 2、在class的命名空间内提供一个non-member swap,并用它调用上诉swap成员函数。3、如果正在编写一个class,为class特化std::swap,并用它调用你的swap成员函数。