C++设计与声明超详细讲解
作者:RolleX
让接口被正确使用不易被误用
除非有好的理由,否则应该让你的types的行为与内置types一致,请拿ints做范本
提供行为一致的接口!
阻止误用的办法包括建立新类型,限制类型上的操作,束缚对象值(比如要统计年月日,限制月的大小在1-12),消除客户的资源管理责任(智能指针)
shared_ptr支持定制型删除器,可被用来自动解除互斥锁等
宁以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引用传递好!
必须返回对象时
别妄想返回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对象而有可能同时需要多个这样的对象(一个指针修改了指向对象的参数后,其他指针指向的参数也被修改了)
将成员变量声明为private
语法一致性:成员变量不是public,用户只能通过public里的相应函数来访问成员变量,用户使用时就都有一致的使用规则(全都要使用小括号等)
使用函数可以对成员变量的处理有更精确的控制,如可以编写const函数实现只读访问,不加const实现读写访问等
封装性,防止成员变量被更改
假如有一个public成员变量,我们最终取消了它,所有使用它的代码都会被破坏,假如有一个protected成员变量,我们最终取消了它,所有使用它的派生类都会被破坏。因此protected其实并不比public更加具有封装性
说到底,选择private就好
以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等,导入头文件再进行调用,即可完成很多事情
non-member
若所有参数皆需要类型转换,请为此采用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
考虑写出一个不抛出异常的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成员函数。
到此这篇关于C++设计与声明超详细讲解的文章就介绍到这了,更多相关C++设计与声明内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!