EC++学习笔记(四) 设计与声明
条款18:让接口容易被正确使用,不易被误用
必须考虑客户可能做出什么样的错误(防御式编程)
std:shared_ptr会自动使用它的"每个指针专属的删除器",消除了"cross-DLL problem"(对象在DLL中被 new 创建,却在另一个DLL中 delete 销毁)
条款19:设计 class 犹如设计 type 类型系统
条款20:宁以 pass-by-reference-to-const 代替 pass-by-value
C语言永远是pass-by-value,C++默认是pass-by-value,函数参数都是以实际参数的副本拷贝为初值,函数调用的结果是函数返回值的一个副本拷贝
注意C语言中传指针方式,传指针具有引用语义,对象复制以后并没有分离,而是共享关联同一资源
采用pass-by-reference-to-const 效率高(无对象拷贝),是多态前提(基类引用),也可以避免基类和派生类之间的对象切割
条款21:必须返回局部对象时,禁止返回其reference
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对象在函数退出前已经被销毁了!!!
任何函数如果返回一个reference指向某个local对象都将一败涂地
任何函数如果返回一个指针指向某个local对象同样将一败涂地
const Rational& operator*(const Rational& lhs, const Rational& rhs) {
static Rational result;
result = ...;
return result;
}
这个函数返回static对象,不具有多线程安全性,并行计算时极易出错
必须返回对象时,就让函数pass-by-value返回一个新对象
inline const Rational operator*(const Rational& lhs, const Rational& rhs) {
Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
这个函数以pass-by-value方式返回值语义对象,值语义对象复制之后两者分离,所以函数结束后获得的仍然是正确的对象
总结:绝不要返回reference或pointer指向一个local对象
条款22:将成员变量声明为 private
客户唯一能够访问对象成员变量的方法就是通过存取函数get()和set()
成员变量的封装性与"成员变量的内容改变时所破坏的代码量"成反比
条款23:宁以non-member、non-friend 替换member函数(本人不认同)
条款24:操作符重载时必须选择设置为类成员还是普通的非成员函数
操作符重载时,选择成员还是非成员实现有一些指导原则:
1.赋值(=)、下标([])、调用( () )、成员访问(->)必须定义为类成员,否则编译出错
2.复合赋值操作符(+=)通常定义为类成员
3.对称操作符(算术运算符、关系运算符)通常选择为non-member函数实现
注:操作符选择类成员实现时,显式形参数比操作数数目少1,因为当操作符为成员函数时,this 指向左操作数
条款25:考虑写一个不抛异常的 swap 函数
STL提供的标准swap算法如下:
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;
std::vector<double> v;
}; class Widget{
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs) {
...
*pImpl = *(rhs.pImpl);
}
private:
WidgetImpl* pImpl;
};
一旦置换两个Widget对象,唯一需要做的就是置换其pImpl指针,可以考虑模板特化技术
namespace std{
template<>
void swap<Widget>(Widget& a, Widget& b) {
swap(a.pImpl, b.pImpl); //目前还无法编译通过
}
}
上述代码中 template<> 表示它是 std:swap的一个全特化版本,函数名之后的<Widget>表示这一特化版本只针对"T是Widget"而设计(偏特化)
上述代码不能通过编译,因为a.pImpl和b.pImpl都是 private,所以进行如下改进:
class Widget{
public:
...
void swap(Widget& other) { //绝不可以抛出异常
using std::swap; //逼迫编译器使用std::swap(因为偏特化版本swap还未实现完成)
swap(pImpl, other.pImpl); //此时使用的是std::swap
}
} namespace std{
template<>
void swap<Widget>(Widget& a, Widget& b) {
a.swap(b);
}
}
一旦编译器看到对swap调用时,就会查找适当的swap调用,如果没有T专属之swap存在,编译器就会使用std内的swap
std::swap(obj1, obj2); //错误的swap调用方式,强迫编译器总是调用std内部的swap版本
swap(obj1, obj2); //正确的swap调用方式,不带任何的命名空间修饰
c++只允许对类模板进行偏特化,不能对函数模板进行偏特化
最新文章
- FragmentPagerAdapter+ViewPager+Fragment
- pdsh使用
- ValidationSummary控件不弹出错误提示框
- 标准库shared_ptr智能指针的实现
- MBProgressHUD ---
- tomcat的几种部署方法
- c语言题目:找出一个二维数组的“鞍点”,即该位置上的元素在该行上最大,在该列上最小。也可能没有鞍点
- 学SpringMVC收藏
- 精通CSS+DIV网页样式与布局--制作实用菜单
- Shiro安全框架【快速入门】就这一篇!
- Step by Step Recipe for Securing Kafka with Kerberos
- [转]angular2: including thirdparty js scripts in component
- mvc未登录跳转到登录界面
- <;亲测>;CentOS7中使用yum安装Nginx的方法
- Coursera台大机器学习技法课程笔记08-Adaptive Boosting
- CentOS6.6安装S******Sockets服务端
- linux列出一个目录及其子目录下面的某种类型的文件
- AMQP学习 &; RabbitMQ 与 ActiveMQ、ZeroMQ以及Kafka的比较
- 数学图形(2.20)3D曲线
- os模块、文件压缩 、匹配文件后缀名:fnmatch glob