Effective C++笔记04:设计与声明
条款18:让接口easy被正确使用,不易被误用
1,好的接口非常easy被正确使用,不easy被误用。你应该在你的全部接口中努力达成这些性质。
2,“促进正使用”的办法包含接口的一致性,以及与内置类型的行为兼容。
3,“阻止误用”的办法包含建立新类型,限制类型上的操作,束缚对象值,以及消除客户的资源管理责任。
4,shared_ptr支持定制型删除器。这能够防范DLL问题,能够用来自己主动解除相互排斥锁。
条款19:设计class宛如设计type
博客地址:http://blog.csdn.net/cv_ronny转载请注明出处!
怎样设计你的类:
1,新type的对象应该怎样创建和销毁?
影响到的设计函数:构造函数、析构函数以有内存分配函数和释放函数(operator new,operator new[],operator delete,operator delete [])。
2,对象初始化和对象的赋值有什么样的区别?
取决于构造函数和赋值操作符的行为。
3,新type对象假设被passed-by-value,意味着什么?
拷贝构造函数用来定义一个type的pass-by-value该假设实现。
4,什么是新type的“合法值”?
构造函数必须进行有效的检測。
5,你的新type须要配合某个继承图系吗?
假设继承自其它的类,要受到其它类的束缚,注意virtual和no-virtual的影响。假设有其它类继承这个类,那么须要考虑是否要把析构函数设计为virtual函数。
6,你的新type须要什么样的转换?
如要希望其它类型能转换为你所设计的type类型,则须要相应的non-explicit构造函数存在。假设须要你把设计的type能够转换为其它类型,则须要定义类型转换函数operator T。
7,什么样的操作和函数对此新type而言是合理的。
取决于你为你的class声明哪些函数。当中有些可能是成员函数,有些则不是。
8,什么样的标准使函数应该驳回?
那些正是你必须声明为private者。
9,谁该取用新type的成员?
这个提问帮组你决定哪个成员为public,哪个为protected,哪个为private。它也帮助你决定哪一个class或function应该是友元,以及将它们嵌套于还有一个之内是否合理。
10,你的新type有多么一般化?
假设你定义的是一整个types,是否应该定义一个新的class template 。
11,什么是新type的“未声明接口”?
12,你真的须要一个新type吗
假设仅仅是定义新的derived class以便为既有的类加入机能,那么说不定单纯定义一个或多个非成员函数或模板,更可以达到目标。
条款20:宁以pass-by-reference-to-const替换pass-by-value
pass-by-value会造成较多的构造函数与析构函数的开销,而且在将派生类传递给基类接口的时候会发生类的分割问题。
上面的规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-vaule往往比較适当。
条款21:必须返回对象时,别妄想返回其引用
绝对不要返回point或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回point或reference指向一个local static对象而有可能同一时候须要多个这种对象。
我们来考虑一个有理数的类以及为它定义一个有理数想乘的友元:
// 博客地址:http://blog.csdn.net/cv_ronny
class Rational{
public:
Rational(int numerator = 0, int denominator = 1);
private:
int n, d;
friend const Rational operator*(const Rational& lhs, const Rational& rhs);
};
如今我们来设计这个友元函数,它的功能是返回两个Rational对象的乘积,我们有3种方案:
// 方案一
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
Rational result(lhs.n*rhs.n, lhs.d*rhs.d);
return result;
}
这个函数返回了result的引用,可是result是一个local对象,调用函数结束时,该对象会被销毁,而它的引用也就毫无意义指向了没有定义的对象。
// 方案二
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
Rational* result=new Rational(lhs.n*rhs.n, lhs.d*rhs.d);
return *result;
}
方案中result并非一个local对象,而是一个从heap中动态分配得到的对象的指针,那么这种问题是谁来负责delete这个指针呢,假如有这种表达式:
Rational w, x, y, z;
w = x*y*z;
这面的表达式事实上调用了两次operator*操作,也就是创建了两次动态内存区域,可是没有办法取得它们的指针。
// 方案三
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
static Rational result;
result = Rational(lhs.n*rhs.n, lhs.d*rhs.d);
return result;
}
这次是通过static对象使得result脱离函数后依旧存在,可是就像全部的static对象设计一样,这一个也立马造成我们对多线程安全性的疑虑。并且假设有以下的代码:
bool operator==(const Rational& lhs, const Rational& rhs);
Rational a, b, c, d;
if ((a*b) == (c*d)){}
else{}
上面代码里的if后的条件总是成立的,由于a*b返回的一个static对象的引用,而c*d返回的是同一个static对象的引用,所以永远相等。
所以,我们终于的选择是通过pass-by-value来返回新的对象。
const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.n*rhs.n, lhs.d*rhs.d);
}
条款22:将成员变量声明为private
首先是代码的一致性(调用public成员时不用考虑是成员还是函数)。
其次封装性,都写成函数进行訪问能够提供以后改动訪问方法的可能性,而不影响用法。另外,public影响的是全部使用者,而protected影响的是全部继承者,都影响巨大,所以都不建议声明成员变量。
切记将成员变量声明为private。这可赋予客户訪问数据的一致性、可细微划分訪问控制、允诺条件获得保证,并提供class作者以充分的实现弹性。
protected并不比public更具封装性。
条款23:宁以non-member、non-friend替换member函数
想像有个class用来表示网页浏览器。这种class可能提供的众多函数中,有一个用来清除下载元素的快速缓冲区(cache of downloaded elements)、清除訪问过的URLs的历史记录、以及移除系统中全部的cookies。
class WebBrowse
{
public:
void clearCache();
void clearHistroy();
void removeCookies();
};
很多用户想一个函数来运行整个动作,因些WebBrowse也提供这样一个函数:
class WebBrowse
{
public:
//...
void clearEverything();//依次调用clearCache(),clearHistory(),removeCookies()
};
当然这个功能也能够由一个non-member函数来完毕:
void clearEverything(WebBrowse& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
那么哪一个比較好呢?
依据面向对象守则要求,数据以及操作的那些函数应该捆绑在一块,这意味着member函数是较好的选择。不幸的是这个建议不对。面向对象要求数据应该尽可能被封装。
member函数带来的封闭性比non-member函数低,由于它并不添加“可以訪问class内之private成份”的函数数量。
此外,提供non-member函数能够同意对WebBrowse相关机能能有较大的包裹弹性,而那终于导致较低的编译相依度,添加WebBrowse的可延伸性。
假设我们把WebBrowse相关的功能设计为non-member函数,那么能够将其功能相关的函数的声明单独放在一个头文件中,然后在一个命名空间下。这时候假设我们相扩展相关的这些功能,仅仅须要像其它功能函数一样把声明入在头文件中就可以。
而这样的分割方式并不适用于class成员函数,由于一个class必须总体定义,不能被分割为片断。
请记住
宁可拿non-member non-friend函数替换member函数。这样做能够添加封装性、包裹弹性和机能扩充性。
条款24:若全部參数皆须要类型转换,请为些採用non-member函数
当我们为一个有理数类的设计一个乘法操作符的重载函数,假设我们把它作为类的成员:
class Rational
{
public:
//...
const Rational operator*(const Rational &lhs);
};
当我们尝试混合算术的时候,你会发现仅仅有一半行得通:
Rational result, oneHalf;
result = oneHalf * 2;
result = 2 * oneHalf;
上面第二个赋值语句是错误的,由于它等价于result=2.operator*(oneHalf),这显然是错误的。
而第2条语句等价于result=oneHalf.operator(2),它能够运行成功是由于2发生了隐式类型转换,由于Rational有一个non-explicit的接受int形參的构造函数。
所以,假设我们想运行上面的操作,我们须要将这个重载函数设计为non-member函数。
const Rational operator*(const Rational& lhs, const Rational& rhs);
假设你须要为某个函数的全部參数(包含被this指针所指向的那个隐喻參数)进行类型转换,那么这个函数必须是个non-member。
条款25:考虑写出一个不抛异常的swap函数
1,当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
2,假设你提供一个member swap,也该提供一个non-member swap来调用前者,对于classes(而非templates),也请特化std::swap。
3,调用swap时应针对std::swap使用using声明式,然后调用swap而且不带不论什么“命名空间资格修饰”。
4,为“用户定义类型”进行std template全特化是好的,但千万不要尝试在std内增加某些对std而言全新的东西。
Effective C++笔记04:设计与声明的更多相关文章
- Effective C++笔记:设计与声明
条款18:让接口容易被正确使用,不易被误用 1,好的接口很容易被正确使用,不容易被误用.你应该在你的所有接口中努力达成这些性质. 2,“促进正使用”的办法包括接口的一致性,以及与内置类型的行为兼容. ...
- Effective C++: 04设计与声明
18:让接口容易被正确使用,不易被误用 1:理想上,如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不该通过编译:如果代码通过了编译,它的作为就该是客户所想要的. 2:许多客户端的错误可以 ...
- EC++学习笔记(四) 设计与声明
条款18:让接口容易被正确使用,不易被误用 必须考虑客户可能做出什么样的错误(防御式编程)std:shared_ptr会自动使用它的"每个指针专属的删除器",消除了"cr ...
- 《Effective C++》第4章 设计与声明(2)-读书笔记
章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...
- 《Effective C++》第4章 设计与声明(1)-读书笔记
章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...
- Item 5:那些被C++默默地声明和调用的函数 Effective C++笔记
Item 5: Know what functions C++ silently writes and calls 在C++中,编译器会自己主动生成一些你没有显式定义的函数,它们包含:构造函数.析构函 ...
- 《Effective C++》阅读总结(四): 设计、声明与实现
第四章: 设计与声明 18. 让接口更容易被正确使用,不易被误用 将你的class的public接口设计的符合class所扮演的角色,必要时不仅对传参类型限制,还对传参的值域进一步限制. 19. 设计 ...
- Effective Java笔记一 创建和销毁对象
Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...
- java effective 读书笔记
java effective 读书笔记 []创建和销毁对象 静态工厂方法 就是“封装了底层 暴露出一个访问接口 ” 门面模式 多参数时 用构建器,就是用个内部类 再让内部类提供构造好的对象 枚举 si ...
随机推荐
- 从SVN导出指定版本号之间修改的文件(转)
从SVN导出指定版本号之间修改的文件(转) 当一个网站项目进入运营维护阶段以后,不会再频繁地更新全部源文件到服务器,这个时间的修改大多是局部的,因此更新文件只需更新修改过的文件,其他没有修改过的文 ...
- STL中用erase()方法遍历删除元素 .xml
pre{ line-height:1; color:#f0caa6; background-color:#2d161d; font-size:16px;}.sysFunc{color:#e54ae9; ...
- 捣蛋phpwind过滤器执行流程
从上一篇我们就大概就知道过滤器的定义和怎样去配置,这一节来说说执行流程 public function run($handlerAdapter = null) { $handlerAdapter != ...
- Hadoop学习笔记(7) ——高级编程
Hadoop学习笔记(7) ——高级编程 从前面的学习中,我们了解到了MapReduce整个过程需要经过以下几个步骤: 1.输入(input):将输入数据分成一个个split,并将split进一步拆成 ...
- mysql create table - data_type length -- clwu
mysql create table 时,有时需要指定 data_type length http://dev.mysql.com/doc/refman/5.5/en/create-table.ht ...
- permission denied部署django 遇到没有python_egg_cache的问题解决
检查/etc/httpd/logs/error_log,看是否有如下错误: [Errno 13] Permission denied: '/var/www/.python-eggs' 这时候需要编辑“ ...
- Cocos2d-x项目移植到WP8小记
Cocos2d-x项目移植到WP8小记 作者: K.C. 日期: 10/24/2013 Date: 2013-10-24 00:33 Title: Cocos2d-x项目移植到WP8小记 Tags: ...
- POJ 1066 Treasure Hunt(线段相交判断)
Treasure Hunt Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 4797 Accepted: 1998 Des ...
- Wicket Hello World Example
A simple hello world example in Wicket, show the basic structure of Wicket web application. Tools an ...
- 原生的AJAX写法,可以直接复制过来套用
方法一: function createXMLHTTPRequest() { //1.创建XMLHttpRequest对象 //这是XMLHttpReuquest对象无部使用中最复杂的一步 //需要针 ...