条款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:设计与声明的更多相关文章

  1. Effective C++笔记:设计与声明

    条款18:让接口容易被正确使用,不易被误用 1,好的接口很容易被正确使用,不容易被误用.你应该在你的所有接口中努力达成这些性质. 2,“促进正使用”的办法包括接口的一致性,以及与内置类型的行为兼容. ...

  2. Effective C++: 04设计与声明

    18:让接口容易被正确使用,不易被误用 1:理想上,如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不该通过编译:如果代码通过了编译,它的作为就该是客户所想要的. 2:许多客户端的错误可以 ...

  3. EC++学习笔记(四) 设计与声明

    条款18:让接口容易被正确使用,不易被误用 必须考虑客户可能做出什么样的错误(防御式编程)std:shared_ptr会自动使用它的"每个指针专属的删除器",消除了"cr ...

  4. 《Effective C++》第4章 设计与声明(2)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  5. 《Effective C++》第4章 设计与声明(1)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  6. Item 5:那些被C++默默地声明和调用的函数 Effective C++笔记

    Item 5: Know what functions C++ silently writes and calls 在C++中,编译器会自己主动生成一些你没有显式定义的函数,它们包含:构造函数.析构函 ...

  7. 《Effective C++》阅读总结(四): 设计、声明与实现

    第四章: 设计与声明 18. 让接口更容易被正确使用,不易被误用 将你的class的public接口设计的符合class所扮演的角色,必要时不仅对传参类型限制,还对传参的值域进一步限制. 19. 设计 ...

  8. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  9. java effective 读书笔记

    java effective 读书笔记 []创建和销毁对象 静态工厂方法 就是“封装了底层 暴露出一个访问接口 ” 门面模式 多参数时 用构建器,就是用个内部类 再让内部类提供构造好的对象 枚举 si ...

随机推荐

  1. collect my database for test KCF tracker tools

    Path Button used to set dir where avi file saves, set path set video size and start record write to ...

  2. 判断CString字符串中各位是数字,大小写字母,符号,汉字.xml

    pre{ line-height:1; color:#1e1e1e; background-color:#e9e9ff; font-size:16px;}.sysFunc{color:#627cf6; ...

  3. 【LeetCode】96 - Unique Binary Search Trees

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  4. 【LeetCode】242 - Valid Anagram

    Given two strings s and t, write a function to determine if t is an anagram of s. For example,s = &q ...

  5. Python中list的实现

    原文链接这篇文章介绍了Python中list是如何实现的.在Python中list特别有用.让我们来看下list的内部是如何实现的.来看下面简单的程序,在list中添加一些整数并将他们打印出来. &g ...

  6. Java实现中文字符串的排序功能

    package test; /** * * @Title 书的信息类 * @author LR * @version 1.0 * @since 2016-04-21 */ public class B ...

  7. UIActivityViewController 自定义选项

    UIActivityViewController 自定义选项 重写 UIActivity 类 建议下载github上源码学习一下 https://github.com/samvermette/SVWe ...

  8. 关于javascript里面仿python切片操作数组的使用方法

    其实在使用了好一段时间的 python之后,我觉得最让我念念不忘的并不是python每次在写函数或者循环的时候可以少用{}括号这样的东西(ps:其实也是了..感觉很清爽,而且又开始写js的时候老是想用 ...

  9. Python 代码性能优化技巧(转)

    原文:Python 代码性能优化技巧 Python 代码优化常见技巧 代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构.优化. ...

  10. Accessor Search Implementation Details

    [Accessor Search Implementation Details] Key-value coding attempts to use accessor methods to get an ...