C++ 设计与声明原则
来源:互联网 发布:什么是淘宝软文 编辑:程序博客网 时间:2024/05/21 13:57
Rule18:让接口容易被正确使用,不易被误用
Make interfaces easy to use correctly and hard to use incorrectly
欲开发一个“容易被正确使用,不容易被误用”的接口,首先必须考虑客户可能做出什么样的错误。假设你为一个用来表现日期的class设计构造函数:
class Date{ public: Date(int month,int day,int year); }
客户很容易犯下错误,比如会以错误的次序传递参数,或者传递无效的值。
可以导入新类型而获得上述情况的预防。
比如:
struct Day { explicit Day(int d):val(d){} int val; } struct Month { explicit Month(int d):val(d){} int val; } struct Year { explicit Year(int d):val(d){} int val; } class Date{ public : Date(const Month& m,const Day& d,const Year&y); }
设计class犹如设计type
treat class design as type design
设计一个新的类,应该带着和“语言设计者当初设计语言内置类型时”一样的谨慎态度。
以下几个是设计时常需要面临的问题:- 新type的对象应该如何被创建和销毁?
- 对象的初始化和对象的赋值该有什么样的差别?
- 新type的对象如果被passed by value,意味着什么?记住,copy构造函数来定义一个type的pass-by-value该如何实现。
- 什么是新type的合法值?有时候要设置数据值的约束条件,也就是说成员函数必须进行某些错误检查工作。
- 谁该取用新的type的成员? 这个问题可以帮助你决定哪些成员为public,protected,private。
宁以pass-by-reference-to-const替换pass-by-value
默认情况下,函数参数都是以实际实参的副本为初值,而调用端所获得的亦是函数返回值的一个副本。这些副本右对象的拷贝构造函数产生,这可能使得pass-by-value成为费时的操作。
使用 bool validateStudent(const Student& s);这种传递方式的效率高得多,没有任何构造函数或析构函数被调用,因为没有任何新对象被创建。修订后的这个参数声明中的const是重要的。因为如果是值传递,函数中对对象进行修改不会影响原对象,只会对副本做修改。现在Student以by reference方式传递,将它声明为const是必须的,因为不这样调用者会担忧validateStudent会不会改变他们传入的那个Student。
以by reference方式传递参数也可以避免slicing(对象切割)问题。
现在假设希望写个函数打印窗口名称,然后显示该窗口。
void printNameAndDisplay(Window w) { std::cout<<w.name(); w.display(); }
当你想调用上述函数,并传递WindowWithScrollBars对象时,参数w会被构造成为一个Windows对象,因为他是一个Window对象。 他的继承特征信息全部会被切除为基类特征。解决切割问题的办法就是以by reference-to-const的方式传递w。
- 必须返回对象时,别妄想返回其reference
don’t try to return a reference when you must return an object
因为有时会犯下一个致命错误:开始传递一些references指向其实并不存在的对象。
函数创建新对象的途径有二:在stack空间或在heap空间创建之,如果定义一个local变量,就是在stack空间创建对象。比如下面代码:
const Rational& operator*(const Rational& lhs,const Rational& rhs) { Rational result(lhs.n*rhs.n,lhs.d*rhs.d); return result; }
这个函数返回一个reference指向result。但是result是个local对象,而local对象在函数退出前被销毁了。
于是,可能会考虑在heap内构造一个对象,并返回reference指向它。heap-based对象由new创建,所以heap-based代码如下:
const Rational& operator*(const Rational& lhs) { Rational* result = new Rational(lhs.n*this->n,lhs.d*this->d); return result; }
存在的问题是?谁该对new出来的对象实施delete操作?
上述不论on-the-stack或on-the-heap做法,都因为对operator*返回的结果调用构造函数而受惩罚。所以正确做法就是让那个函数返回一个新的对象。
inline const Rational operator *(const Rational& lhs){return Rational(this->n*lhs.n,this->d*lhs*d);}
- 将成员变量声明为private
注一下如何使用特化版本的函数模板
比如有一个函数模板如下:
namespace std { template<class T> void swap(T& a,T& b) { T temp(a); a = b; b= temp; } }
有时我们需要特化这个函数模板,即当某一类比如叫Widget调用swap的时候不要使用原始模板的方法(这种复制操作效率有时可以进行优化,比如Widget希望自己实现,直接调换所指向的指针)
写法就像如下的形式:
namespace std{ template<> void swap<Widget>(Widget& a,Widget &b) { swap(a.pImpl,b.pImpl);//若要置换Widgets就置换其pImpl指针 }}
但是,pImpl成员变量如果是私有(大多时候也是私有的),无法访问。我们:令Widget声明一个名为swap的public成员函数做真正的置换工作,然后将std::swap特化,令他调用该成员函数。
class Widget{ public: .... void swap(Widget& other){using std::swap;swap(pImpl,other.pImpl);}...};namesapce std{template<>void swap<Widget>(Widget &a,Widget &b){ a.swap(b); //若要置换Widgets,调用其swap成员函数}}
- C++ 设计与声明原则
- Effective C++(四)设计与声明
- effective C++: 4.设计与声明
- Effective C++(四)接口设计与声明
- <<Effective C++>>读书笔记4: 设计与声明
- 《Effective C++》设计与声明章节
- 《Effective C++》第四章:设计与声明
- 设计模式原则与思想(c#)
- c语言声明优先原则
- 【读书笔记】Effective C++—4 设计与声明(之1)
- 一个C语言声明解析器的设计与实现
- 《Effective C++》设计与声明:条款18-条款19
- 如何写出高效C++(设计与声明)
- C++(4)设计与声明
- 设计与声明
- 4.设计与声明
- 4 设计与声明
- C语言头文件组织与包含原则(函数指针和结构体的前项声明)
- R.String.xxx
- 截取指定字符前后的指定长度的字符,得到新的字符串
- Tomcat启动报Error listenerStart错误
- ugui 点击物品后创建实例并拖拽
- 由一次merge错误引发git 分支模型的思考
- C++ 设计与声明原则
- git配置用户名和邮箱
- 矩阵及其基本运算
- 好长时间没更新了..
- Consul入门06 - 键/值对数据
- Problem-D
- HTML5 canvas画布(五)
- 001-整数快速幂-归纳法-《算法设计技巧与分析》M.H.A学习笔记
- POJ1273 Drainage Ditches 【最大流】