设计与声明

来源:互联网 发布: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而言全新的东西。

原创粉丝点击