effective c++ 笔记 (23-25)
//---------------------------15/04/08----------------------------
//#23 宁以non_member、non_friend替换member函数
{
class WebBrowser
{
public:
...
void clearCache();
void clearHistory();
void removeCookies();
...
};
/* 对于上面这个函数,有的客户会想一次调用这三个函数,那么时使用member函数好呢,
还是non_menber non_friend函数好呢
1:对于封装性来说,是non_member版本比较好。
1>从封装开始讨论:如果某东西被封装,它就不再可见。愈多东西被封装,愈少人可以看到它。
而愈少人看到它,就有愈大的弹性去变化它,因为我们的改变仅仅直接影响看到改变的那些人事物。
就这一点来说,能够访问private成员变量的就是成员函数和friend函数。如果non_member和
member函数提供的功能是相同的,那么,封装性较大的是non_member函数,因为它不增加”能访问
class内private成分“的数量。
2>member函数说的只是针对能访问到private成员的类,并不是说这个函数不能是别的类的member函数。
2:把这些non_member函数放入namespace并分类到不同的头文件中。这么做的原因是,客户可以针对不同的
功能,包含不同的头文件。
*/
}
//#24 若所有参数皆需类型转换,请为此采用non_member函数
{
// 令class支持隐式类型转换通常是个糟糕的主意。然而也有例外,最常见的例外是在建立数值类型时。
// 如果要设计一个有理数的类,让他支持int隐式转换到有理数还是很合理的
class Rational
{
public:
Rational(,
)//没有使用explicit,允许隐式转化
int numerator()
const;
int denominator()
const;
private:
};
// 让有理数类支持+ *等操作时,使用non_member函数实现,类内部的operatpr*()操作无法实现全部功能。
const Rational
operator*(const Rational& rhs)const;
Rational oneHalf(,),result;
; //可以正确调用operator*()操作,2在参数列表中,可以隐式转换
result = * oneHalf;
//错误,没有这样的操作
// 结论:只有当参数被列于参数列表内,这个参数才是隐式类型转换的合格参与者。
// 所以,只能使用non_member函数,把两个数都放入参数列表中
const Rational
operator*(const Rational& lhs,const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
// 是否让它成为friend的?没有必要,不是friend就能完成任务,就不必成为friend了
}
//#25 考虑写出一个不抛异常的swap函数
{
// swap原本是stl的函数:
namespace std
{
template<typename T>
void swap(T& a, T& b)
{
T temp(a);
a = b;
b = temp;
}
}
// 这种调用方式,在某些情况,会产生不必要的消耗:
class WidgetImpl
{
public:
...
private:
int a,b,c;
stad::vector<double> v;//意味着复制需要的时间很长
};
class Widget
{
public:
Widget(const Widget& rhs);
Widget&operator=(const Widget& rhs)
{
...
*pImpl = *(rhs.pImpl); //深拷贝
...
}
private:
WidgetImpl* pImpl;
};
// 当执行swap的时候,正常情况应该直接交换两个 pImpl的指针,而stl版本的却要复制三个widgetImpl对象
// 为了特化,可以声明一个成员函数对指针调用swap函数:
class Widget
{
public:
void swap(Widget& other)
{
using std::swap;
swap(pImpl, other.pImpl);
}
...
};
namespace std
{
template<>
void swap<Widget>(Widget& a, Widget& b)
{
a.swap(b);
}
}
// 当Widget时个class template时,这么做是错的:
namespace std
{
template<typename T>
void swap< Widget<T> >(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
}
}
// 这样是在偏特化 swap的函数模版参数,并不是偏特化swap函数,所以可以使用重载:
namespace std
{
template<typename T>
void swap(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
}
}
// 然而我们不能往std中添加新的东西。所以我们可以把non_member函数放到Widget的namespace那儿
namespace WidgetStuff
{
template<typename T>
class Widget{...};
...
template<typename T>
void swap(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
}
}
// 使用
template<typename T>
void doSomething(T& obj1, T& obj2)
{
using std::swap;
//为了std的swap可见,至于调不调用是编译器的事
...
swap(obj1, obj2);
}
/* swap总结:
1>提供一个public swap成员函数,让它高效地置换你的类型的两个对象值。
2>在你的class或template所在的命名空间内提供一个non_member swap,并令它调用上述swap成员函数
3>如果你编写的class不是class template。为你的class特化std::swap,并令让调用swap成员函数。
4>在使用swap时,记得使用 using std::swap,使得std::swap可见。
5>成员版swap绝对不能抛出异常。
*/
}
effective c++ 笔记 (23-25)的更多相关文章
- [Effective JavaScript 笔记]第25条:使用bind方法提取具有确定接收者的方法
js里方法和属性值为函数,就像一个东西两种称呼一个样,比如土豆,也叫马铃薯,一个样.既然一样,那就可以对对象的方法提取出来为函数,然后把提取出来的函数作为回调函数直接传递给高阶函数. 高阶函数是什么 ...
- [Effective JavaScript 笔记]第3章:使用函数--个人总结
前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...
- Item 5:那些被C++默默地声明和调用的函数 Effective C++笔记
Item 5: Know what functions C++ silently writes and calls 在C++中,编译器会自己主动生成一些你没有显式定义的函数,它们包含:构造函数.析构函 ...
- 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+ ...
- java effective 读书笔记
java effective 读书笔记 []创建和销毁对象 静态工厂方法 就是“封装了底层 暴露出一个访问接口 ” 门面模式 多参数时 用构建器,就是用个内部类 再让内部类提供构造好的对象 枚举 si ...
- Ext.Net学习笔记23:Ext.Net TabPanel用法详解
Ext.Net学习笔记23:Ext.Net TabPanel用法详解 上面的图片中给出了TabPanel的一个效果图,我们来看一下代码: <ext:TabPanel runat="se ...
随机推荐
- SQLSERVER中的鬼影索引
SQLSERVER中的鬼影索引 看这篇文章之前可以先看一下鬼影记录 了解了解一下SQLSERVER里的鬼影记录关于鬼影记录的翻译一关于鬼影记录的翻译二 当删除表中的某一条记录的时候,索引页面的对应记录 ...
- axios的get,post方法
学习vue和nodejs的过程当中,涉及到了axios,今天为了测试,写了get和post两个方法来跟node服务端交互,结果因为header和参数弄了好久,在此记录一下,同时分享; 由于刚接触axi ...
- 破解 jar 包之直接修改 .class 文件方式
一.常规 JAVA 软件破解流程 先讲一下常规jar包的破解流程. 1. 快速定位. 1) 通过procmon监控相关软件,查看程序都访问了些啥. 2) 用jd-gu ...
- 免安装PostgreSQL启动服务及创建数据库
安装环境windows7 64位系统 免安装版本postgresql-10.3-1-windows-x64-binaries ===================================== ...
- ssh中文手册
ssh-keygen 中文手册 sshd_config 中文手册 sshd 中文手册
- cat > file << EOF 与 cat > file << -
当我们在使用kickstart 的时候,会遇到写网卡配置文件的情况,这时候我们使用cat > file << EOF 命令等,可以从标准输入中接受输入并保存到 file 文件中. c ...
- python基础学习20----线程
什么是线程 线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.另外,线程是进程中 ...
- 【redis】redis的雪崩和穿透
1.什么是缓存穿透 一般的缓存系统,都是按照key值去缓存查询,如果不存在对应的value,就应该去DB中查找 .这个时候,如果请求的并发量很大,就会对后端的DB系统造成很大的压力.这就叫做缓存穿透. ...
- Windows窗体数据抓取详解
最近在客户项目上刚好遇到一个问题,项目需求是要获取某台机床的实时状态,问题点刚好就在于该机床不是传统意义上的数控机床,也不是PLC控制器,只有一个上传下载程序文件的应用程序,上面刚好有几个按钮可以大概 ...
- 一次gcc编译失败分析
1. 场景: 在使用gcc编译时,编译器报错:xxx:'xxx'未定义的引用 2. 排查过程: 首先,我很自然的想到是不是自己使用了未定义的函数或者将函数名称写错了,在检查了一边过后,我发现自己使 ...