设计与声明
来源:互联网 发布:dnf为什么不停网络中断 编辑:程序博客网 时间:2024/06/08 02:22
Item18 Make interfaces easy to use correctly and hard to use incorrectly.
程序员在自定义接口的时候,要注意与内置行为的类型保持兼容性。
容易正确使用,举例如下:
class Date{public:Date(int month,int day,int year);};
不如下面的形式,防止传错参数,并且可对传入Month和Day的日期进行检查是否是合理的输入参数:
class Month{public:explicit Month(int m):val(m) {}private:int val;};class Day{public:explicit Day(int d):val(d) {}private:int val;};class Year{public:explicit Year(int y):val(y) {}private:int val;};class Date{public:Date(const Month& m,const Day& d,const Year& y);};
Month的取值是1到12是固定的,可按如下方式给出:
class Month{public:static Month Jan() {return Month(1);}static Month Feb() {return Month(2);}.....static Month Dec() {return Month(12);}private:explicit Month(int m):val(m) {}int val;};
这样在定义一个Date的对象的时候,可如下定义:Date date(Month::Dec(),14,2012);
另外在之前的资源管理的总结中Investment的例子,Invertment* createInvestment();
这需要用户在获取了这个指针后还要记得释放资源,即使采用前面介绍的用对象来管理资源,也还得向用户说明,故不如直接返回给用户一个资源管理对象来的直接,修改如下:
std::tr1::shared_ptr<Investment> createInvestment();
此办法的最大好处是,对于跨DLL的资源管理权责比较清楚。
Item19 Treat class design as type design.
严格程度如同语言的设计者在设计内置数据类型的情况。
在设计类的时候要考虑以下几个设计规范:
(1)新type的对象应该如何被创建和销毁?
(2)对象的初始化和对象的赋值该有什么样的差别?
(3)新type的独享如果被passed by value意味着什么?(copy构造函数用来定义一个type的pass by value该如何实现)
(4)什么是新type的“合法值”。(构造函数、赋值操作符和一些setter函数中,需要对参数进行错误检查)
(5)新type需要配个某个继承关系图吗?设计受已有类的束缚
(6)新type需要什么样的转换?跟其他类型的转换,比如构造函数是否explicit,是否需要提供operator ()等。
(7)什么样的操作符和新函数对新type是合理的?
(8)什么样的标准函数应该驳回?即声明为private。
(9)谁该用新type的成员?可确定哪些成员为public
(10)什么是新type的“未声明接口”? //不太明白书中所指!!!!
(11)新type有多么一般化,是否该定义成class template
(12)真的需要一个新type吗?
Item20 Prefer pass-by-reference-to-const to pass-by-value.
pass-by-value会引起拷贝构造函数和析构函数的调用,而pass-by-reference-to-const则没有。
此规则不适合内置数据类型、STL迭代器和函数对象。
Item21 Don't try to return a reference when you must return an object
class Rational{public:Rational(int numerator=0,int denominator=1):n(numerator),d(denominator) {}private:int n;int d;friend const Rational operator*(const Rational& lhs,const Rational& rhs);};const Rational operator*(const Rational& lhs,const Rational& rhs){return Rational(lhs.n*rhs.n,lhs.d*rhs.d);}
上面的operator*返回一个Rational对象。
如若不然:
const Rational& operator*(const Rational& lhs,const Rational& rhs){Rational result(lhs.n*rhs.n,lhs.d*rhs.d);return result;}
上面的result是一个局部对象,在函数退出时前被销毁了,这个返回至就引用了一个已经释放的对象,情况很糟糕。
Item22 Decare data members private.
这就赋予客户访问数据的一致性,可惜未划分访问控制,允许约束条件获得保证,并提供类的实现者以充分的弹性。注意Protected不必public更具有封装性。
Item 23 Prefer non-member non-friend fuctions to member functions.
non-member non-friend functions可以增加程序的封装性。
例如:
class WebBrowser{public:void clearCache();void clearHistory();void removeCookies();};void clearBrowser(WebBrowser& wb){wb.clearCache();wb.clearHistory();wb.removeCookies();}
如上做法,而不是把clearBrowser定义为WebBrowser的成员。
对于我们程序员来说,一个好的习惯是,对于一个对象的数据,越少的代码可以看到数据,封装性就越好。
Item 24 Decare non-member functions when type conversions should apply to all parameters.
class Rational{public:Rational(int numerator=0,int denominator=1):n(numerator),d(denominator) {}const Rational operator*(const Rational& rhs) const;private:int n;int d;};
operator*若定义成成员,则:
Rational oneEighth(1,8);
Rational oneHalf(1,2);
result=oneHalf*2; //相当于result=oneHalf.operator*(2);可以通过编译。参数2通过隐式的调用构造函数,构造了一个临时的Rational对象。
result=2*oneHalf; //编译不过。
若定义成Item 20中例子的形式,则没有问题。
item25 Consider support for a non-throwing swap.
当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
如果提供了一个member swap,也提供一个non-member swap来调用前者,对于classes(非templates),请特化std::swap。
调用swap时,应针对std::swap调用using声明式,然后调用swap并且不带任何命名空间修饰符。
class WidgetImpl{public: WidgetImpl(int aa,int bb,int cc):a(aa),b(bb),c(cc) {}WidgetImpl(const WidgetImpl& impl){this->a=impl.a;this->b=impl.b;this->c=impl.c;}private:int a,b,c;friend ostream& operator<<(ostream& os,const WidgetImpl& impl){return os<<impl.a<<""<<impl.b<<""<<impl.c;}};class Widget{public:Widget(int a,int b,int c){pImpl=new WidgetImpl(a,b,c);}Widget(const Widget& rhs){pImpl=new WidgetImpl(*(rhs.pImpl));}Widget& operator=(const Widget& rhs){Widget temp(rhs);swap(temp);return *this;}void swap(Widget& other){using std::swap;swap(pImpl,other.pImpl);cout<<"inner swap"<<endl;}~Widget(){if(pImpl!=NULL){delete pImpl;pImpl=NULL;}}private:WidgetImpl* pImpl;friend ostream& operator<<(ostream& os,const Widget& w){return os<<*(w.pImpl);}};namespace std{template <>void swap<Widget>(Widget& a,Widget& b){a.swap(b);}}
如果WidgetImpl和Widget是模板类,则可以添加到一个用户的名字空间中:
namespace WidgetStuff{template <typename T>class WidgetImpl{public: WidgetImpl<T>(T aa,T bb,T cc):a(aa),b(bb),c(cc) {}WidgetImpl<T>(const WidgetImpl<T>& impl){this->a=impl.a;this->b=impl.b;this->c=impl.c;}private:T a,b,c;friend ostream& operator<<(ostream& os,const WidgetImpl<T>& impl){return os<<impl.a<<""<<impl.b<<""<<impl.c;}};template <typename T>class Widget{public:Widget<T>(T a,T b,T c){pImpl=new WidgetImpl<T>(a,b,c);}Widget<T>(const Widget<T>& rhs){pImpl=new WidgetImpl<T>(*(rhs.pImpl));}Widget<T>& operator=(const Widget<T>& rhs){Widget<T> temp(rhs);swap(temp);return *this;}void swap(Widget<T>& other){using std::swap;swap(pImpl,other.pImpl);cout<<"inner swap"<<endl;}~Widget<T>(){if(pImpl!=NULL){delete pImpl;pImpl=NULL;}}private:WidgetImpl<T>* pImpl;friend ostream& operator<<(ostream& os,const Widget<T>& w){return os<<*(w.pImpl);}};template <typename T>void swap(Widget<T>& a,Widget<T>& b){a.swap(b);}}int _tmain(int argc, _TCHAR* argv[]){WidgetStuff::Widget<int> w1(1,2,3),w2(4,5,6);cout<<"before swap"<<endl;cout<<w1<<endl;cout<<w2<<endl;swap(w1,w2);cout<<"after swap"<<endl;cout<<w1<<endl;cout<<w2<<endl;return 0;}
放到名字空间WidgetStuff中,这样防止在std内加入某些对std而言全新的东西。
- 设计与声明
- 4.设计与声明
- 4 设计与声明
- 第四章 设计与声明
- 第四章 设计与声明
- Effective C++ -- 设计与声明
- C++ 设计与声明原则
- 设计与声明(一)
- 设计与声明(二)
- Effective C++笔记: 设计与声明(一)
- Effective C++笔记: 设计与声明(二)
- Effective C++笔记: 设计与声明(三)
- Effective C++笔记: 设计与声明(四)
- Effective C++读书笔记---设计与声明
- Effective C++之设计与声明
- Effective C++(四)设计与声明
- Effective C++ ——设计与声明
- effective C++: 4.设计与声明
- 文档模板遵守标准
- Objective-C 学习记录
- .Android横竖屏的设置和使用
- SVN的标准目录结构:trunk、branches、tags
- XCode4.2免证书真机发布及调试
- 设计与声明
- eclipse 配色网站
- QTreeWidget的一些实用方法
- Eclipse 注释乱码
- PHP获取当前时间差8小时的问题
- android 模拟器与手机的sock通信
- Android HOME键那些事
- 谷歌技术之MapReduce简介
- linux sudo学习