effective c++ 笔记 (18-22)
//---------------------------15/04/06----------------------------
//#18 让接口容易被正确使用,不易被误用
{
// 1:为了防止客户输入错误的参数,可以使用外覆类型来区别:
struct Day
{
explicit Day(int d): val(d) {}
int val;
};
struct Month
{
explicit Month(int m): val(m) {}
int val;
};
struct Year
{
explicit Year(int y): val(y) {}
int val;
};
class Date
{
public:
Date(const Month& m,const Day& d,
const Year& y);
...
};
// 这时,客户职能这么使用Date class:
Date d(Month(), Day(), Year());
// 为了限制类型的值,比如一年只有12个月,Month应该反映这样的事实。
// 为了安全不直接使用enum,而是预先定义所有有效的Months:
class Month
{
public:
);}
);}
...
);}
private:
explicit Month(int m);
};
Date d(Month::Mar(), Day(), Year());
/* 2:预防客户错误的另一个办法是:限制类型内什么事可做,什么事不能做。也就是加上const。
3:除非有好理由,否则应该尽量令你的types的行为与内置types一致
4:任何接口如果要求客户必须记得做某些事情,就是有着“不正确使用”的倾向。
较佳接口的设计原则是先发制人,比如条款13中,
令factory函数返回一个智能指针,可以避免客户忘记使用智能指针: */
std::tr1::shared_ptr<Investment> createInvestment();
// 5:tr1::shared_ptr支持定制型删除器,可以防范DLL问题:
std::tr1::shared_ptr<Investment> createInvestment()
{
std::tr1::shared_ptr<Investment> retval(),
getRidOfInvestment);
retval = ...;
return retval;
}
}
//#19 设计class犹如设计type
{
/* 1:新type的对象应该如何被创建和销毁?
这会影响到你的class的构造函数和析构函数,以及内存分配函数和释放函数。
当然前提是如果你打算撰写他们。
2:对象初始化和对象的赋值该有什么样的差别
这个答案决定了构造函数和赋值操作符的行为,以及差异。
3:新type的对象如果被passed by value,意味着什么?
copy构造函数用来定义一个type的pass by value该如何实现。
4:什么是新type的“合法值”
你的class必须维护自己的约束条件,也就决定了你的成员函数
(特别是构造函数、赋值操作符、和所谓的setter函数)必须进行错误检查工作。
5:你的新type需要配合某个继承图系吗?
继承既有的class会受到哪些class的设计的束缚,特别是他们的函数是virtual或non_virtual的影响
如果允许别人继承自己的类,就会影响你所声明的函数是否为virtual
6:你的新type需要什么样的转换
是否需要隐式转换、显式转换。
7:什么样的操作符和函数对此新type而言是合理的?
这个答案决定你为你的class声明哪些函数,其中某些该是member函数。
8:什么样的标准函数应该驳回
你不想要系统为你声明的函数要声明为private。
9:谁该取用新type的成员
这个答案决定了哪个成员为public,protected,private。以及哪个class或function应该是friend
10:什么是新type的“为声明接口”
它对效率、异常安全性以及资源运用提供何种保证
11:你的新type有多么一般化
看看自己是否应该定义一个新的class template
12:你真的需要一个新type吗
如果只是为既有的class添加机能,那么可能单纯定义一个或多个non member函数或templates
更能达到目标。
*/
}
//#20 宁以pass by reference to const替换pass by value
{
/* 1:pass by value 需要调用copy构造函数产出一个副本,这可能使得pass by value
成为昂贵的操作。如果传递的是一个自定义class,通常需要调用一次copy构造函数加
一次析构函数
如果这个class继承自别的类,又需要多调用好几次这两个函数。
2:by reference方式传递参数可以避免slicing(对象切割)问题:
一个derived class
对象以by value方式传递并被视为一个base class对象时,base class
的copy构造函数被调用,而“造成此对象的行为属于derived class对象”的部分被切掉了,只留
下了一个base class对象。这绝不是你想要的。
3:reference通常以指针来实现出来,所以pass by reference就相当于传递指针。因此如果是一些
内置类型对象(比如int) pass by value往往比 pass by reference的效率高些。
4:除了内置类型
和 stl的迭代器和函数对象,其他的对象都以pass by reference to const
替换 pass by value。
*/
}
//#21 必须返回对象时,别妄想返回其reference
{
// 1:一些值必须返回pass by value:
// 1>通过在stack上创建对象并返回这个对象的引用:
const Rantional&
operator* (const Rantional& lhs,const Rantional& rhs)
{
Rantional result(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
/* 这里有两个点:
1)这样也调用构造了,效率并没提高。
2)返回了一个已经被销毁的对象。严重的错误!
2>通过在堆上创建对象并返回这个对象的引用:
*/
const Rantional&
operator*(const Rantional& lhs,const Rantional& rhs)
{
Rantional result =new Rantional(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
// 这样的话,谁负责delete?而且还是需要一次构造函数。
// 3>使用static对象:
const Rantional&
operator* (const Rantional& lhs,const Rantional& rhs)
{
static Rantional result;
result = ...;
return result;
}
// 这样首先线程不安全,其次使用if((a * b) == (c * d))总是返回true;
// 2:当一个函数必须返回新对象时,就让那个函数返回一个新对象呗!
inline
const Rantionaloperator* (const Rantional& lhs,const Rantional& rhs)
{
return Rantional(lhs.n * rhs.n, lhs.d * rhs.d);
}
}
//#22 将成员变量声明为private
{
/* 1:首先看看成员变量不该是public:
1>一致性:如果没有public成员变量,客户唯一能访问对象的办法就是使用成员函数
客户就不需要在访问成员时疑惑地试着记住是否使用小括号。
2>可以更加精准地控制成员变量:你可以通过函数控制成员变量的读写。
3>封装性:如果通过函数访问成员变量,日后就算更改了这个变量的计算方法,或者直接更改了变量,
客户也不知道,也不必知道。
不封装就意味着不改变。
2:成员变量不该是protected:
道理和public第三点一样,这样对derived class并没有封装性可言
}
effective c++ 笔记 (18-22)的更多相关文章
- [Effective JavaScript 笔记]第22条:使用arguments创建可变参数的函数
第21条讲述使用可变参数的函数average.该函数可处理任意数量的参数并返回这些参数的平均值. 如何创建可变参数的函数 1.实现固定元数的函数 书上的版本 function averageOfArr ...
- [Effective JavaScript 笔记]第3章:使用函数--个人总结
前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...
- java effective 读书笔记
java effective 读书笔记 []创建和销毁对象 静态工厂方法 就是“封装了底层 暴露出一个访问接口 ” 门面模式 多参数时 用构建器,就是用个内部类 再让内部类提供构造好的对象 枚举 si ...
- SQL反模式学习笔记18 减少SQL查询数据,避免使用一条SQL语句解决复杂问题
目标:减少SQL查询数据,避免使用一条SQL语句解决复杂问题 反模式:视图使用一步操作,单个SQL语句解决复杂问题 使用一个查询来获得所有结果的最常见后果就是产生了一个笛卡尔积.导致查询性能降低. 如 ...
- JAVA自学笔记18
JAVA自学笔记18 1.Map接口: 1)功能: 2) Map<String,String>m=new HashMap<String,String>(); //添加元素,元素 ...
- Effective Java笔记一 创建和销毁对象
Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
随机推荐
- IntelliJ IDEA2018激活方法
前言: IntelliJ IDEA2018请在官网下载:https://www.jetbrains.com/idea/ 一.license server激活 输入http://idea.jialeen ...
- iftop – 实时Linux网络带宽监控工具
在本文中,我们提出了另一个称为Interface TOP (IFTOP)的优秀程序, 它是一个基于实时控制台的网络带宽监控工具. 它将显示接口上网络活动的快速概览. Iftop 平均每 2,10 和4 ...
- linux 设备驱动加载的先后顺序
Linux驱动先注册总线,总线上可以先挂device,也可以先挂driver,那么究竟怎么控制先后的顺序呢. 1.初始化宏 Linux系统使用两种方式去加载系统中的模块:动态和静态. 静态加载:将所有 ...
- java基础学习总结——异常处理
一.异常的概念 异常指的是运行期出现的错误,也就是当程序开始执行以后执行期出现的错误.出现错误时观察错误的名字和行号最为重要.
- extern “C”的作用详解
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...
- Audit log report
- 题解 P1550 【[USACO08OCT]打井Watering Hole】
题面(翻译有点问题,最后一句话) 农民John 决定将水引入到他的n(1<=n<=300)个牧场.他准备通过挖若 干井,并在各块田中修筑水道来连通各块田地以供水.在第i 号田中挖一口井需要 ...
- DevExpress06、Popup Menus、RadialMenu、XtraTabControl、SplitContainerControl、GroupControl
Popup Menus 弹出菜单 使用弹出菜单(popup menus),我们可以在 控件上 显示 上下文选项 或 命令. 弹出菜单是一个显示了特定项的窗体,用户可以选择这些项以执行 ...
- 测试dos攻击对openflow中flow_table溢出的影响
环境准备 环境 ubuntu16.04 mininet pox scapy 安装mininet sudo apt-get update sudo apt-get upgrade git clone g ...
- python创建目录保存文件
创建目录 在Python中可以使用os.mkdir()函数创建目录(创建一级目录). 其原型如下所示: os.mkdir(path) 其参数path 为要创建目录的路径. 例如要在D盘下创建hello ...