《C++ Primer》学习笔记【第三部分 类设计者的工具】
第13章 拷贝控制
使用default:=defult只能修饰默认构造函数或拷贝控制成员,显式地要去编译器生成合成的版本。
使用delete:=delete通知编译器不希望定义这些成员,禁止试图使用它的操作,通常的用途是禁止拷贝控制成员,或引导函数匹配。
析构函数不能是delete的,如果删除了析构函数,我们只能动态分配这种类型,并且不能释放这些对象。(非动态类型会被系统自动释放)
定义行为像值的类:如果将一个对象赋予它自己,赋值运算符必须能正确工作(对象内含指针的时候);大多数赋值运算符组合了析构函数和拷贝函数的工作。
class P{
public:
//各个函数
private:
std::string &ps;
int i;
};
P& P::operator = (const P &rhs){
auto newp = new string(*rhs.ps);
delete ps;
ps = newp;
i = rhs.i;
return *this;
}
定义行为像引用的类:指针指向同一块内存
class P{
public:
//各个函数
private:
std::string &ps;//拷贝、赋值时,ps指向同一块内存
int i;
std::size_t *use;//*use表示引用计数,*use = 0时释放内存
};
定义swap:
定义swap的意义在于类中存在指针的话,指针指向的内存可能会再次得到分配(如果定义的是行为像值的类),而实际上交换指针效率更高。
class P{
friend void swap(P& , P&);
//其他成员定义
};
inline void swap(P &lhs, P &rhs){
using std::swap;
swap(lhs.ps, rhs.ps);//使用自己定义的swap交换指针类型, 匹配程度优于std::swap
//交换其他成员
}
//下述代码与默认的swap效率无异,应当使用自己定义的swap
//inline void swap(P &lhs, P &rhs){
// std::swap(lhs.ps, rhs.ps);//使用了标准库的swap
//}
通过swap重载 =
//rhs是按值传递的, 自动处理了自赋值情况且天然就是异常安全的
P& P::operator = (P rhs){
swap(*this, rhs);
return *this;
}
对象移动:
右值引用:必须绑定到右值的引用。右值引用只能绑定到一个将要销毁的对象。
通过右值引用,可以完成移动构造函数和移动赋值运算符。
std::move()获得绑定到左值上的右值引用。
只有当类没有定义自己版本的拷贝控制成员且类的每个非static数据成员都可以移动,编译器才会为它合成移动构造函数或移动赋值运算符。
定义了一个移动构造函数或移动赋值运算符的类必须也定义自己的拷贝操作,否则这些成员默认被定义为删除的。
一些标准库,包括string都定义了移动构造函数。
如果类既有移动构造函数又有拷贝构造函数,则通过函数匹配规则来确定使用哪个构造函数,赋值操作类似;如果没有移动构造函数,右值也被拷贝。
个人感觉,移动构造函数类似于通过把右值的指针赋值过去,并修改右值的指针为空来提高效率。
重载和引用函数:类似于const函数,在参数列表后放置一个引用限定符(&或&&)指出this的左值/右值属性的方式。如果一个成员函数有引用限定符,则具有相同参数列表的所有版本都必须有引用限定符。
第14章 重载运算与类型转换
把二元运算重载为成员函数时,运算的 LHS 是 *this。如果 LHS 为基本类型或是不可修改源代码的类型,就需要重载为非成员函数(如IO类)。
重载为成员函数时,无参则重载的是一元运算符,有1个参数则重载的是二元运算符。
前置++: a.operator++();
后置++: a.operator++(0);
重载成员访问符:P &operator*(), P *operator->(), 解引用通常是类的成员,返回引用;箭头运算符必须是类的成员,必须返回类的指针或重载了箭头运算符的类的对象(即返回的值还能进行箭头运算)。
函数调用运算符:定义了调用操作符的类,其对象称为函数对象,我们说这些对象的行为像函数一样。
struct absInt{
int operator()(int val) const {
return val < ? -val: val;
}
}; absInt absObj;
int x = absObj(-); // x = 10;
函数对象常常作为泛型算法的实参。
sort接受第三个参数为二元谓词函数或函数指针,而priority_queue接受第三个参数为类型。
一般算法接受的是函数对象或函数指针,而容器适配器接受的是一个class类型。
如:
bool cmp(const int &a, const int &b){
return a < b ;
}
struct mycmp {
bool operator() (int i,int j) { return (i<j);}
};
int main(){
bool (*fun)(const int &, const int &) = cmp;
vector<int> ve;
//调用函数指针
sort(ve.begin(), ve.end(), fun);
sort(ve.begin(), ve.end(), cmp);
sort(ve.begin(), ve.end(), mycmp());
//lambda表达式
sort(ve.begin(), ve.end(), [](const int &lhs, const int &rhs){ return lhs < rhs;});
sort(ve.begin(), ve.end(), greater<int>() ); //ve存int, 按降排列, 第3个参数是greater<int>类型的一个未命名对象。
priority_queue<int, vector<int>, greater<int> > Q;
int a = greater<int>()(, ); // a = false, greater<int>是一个类, greater<int>()是一个对象/函数名, greater<int>()(3, 5)是调用函数
return ;
}
标准库定义的函数对象:less<Type>, greater<Type>, plus<Type>等
标准库function类型:function<T> f; 如function<int(int, int)> f1 = add;//声明了一个function类型,可以表示接受两个int,返回一个int的可调用对象。
类型转换运算符:operator type() const;是一种特殊成员函数,复制将类类型的值转换为其他类型。
//构造函数将int转换为类,类型转换符将类转换为int
class SmallInt{
public:
SmallInt(int i = ): val(i){
if(i < ||i > ) throw std::out_of_range("Bad SmallInt value");
};
operator int() const { return val;}
private:
std::size_t val;
}; SmallInt si;
si = ;
si = si+;
注意: int i = 42; cin << i; //cin会转换为bool类型。
为了防止上述情况发生,可用显式的类型转换符,用explicit修饰,如explicit operator int() const { return val;}
但在表达式中被用作条件时,编译器会自动进行显示的类型转换。
operator bool()一般被定义为explicit的。
避免有二义性的类型转换。
第15~16章 略
《C++ Primer》学习笔记【第三部分 类设计者的工具】的更多相关文章
- C++ Primer学习笔记(三) C++中函数是一种类型!!!
C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...
- C++Primer学习笔记《三》
数组名事实上就是一个常指针,指向数组元素中第一个的地址,在程序中假设要用指针遍历数组,不能直接用数组名来自增或自减.由于它是常量,一般先把数组名保存一份同类型的指针,然后再用这个指针来自增或是自减来实 ...
- 深入理解Java虚拟机学习笔记(三)-----类文件结构/虚拟机类加载机制
第6章 类文件结构 1. 无关性 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(即扩展名为 .class 的文件) 是构成平台无关性的基石. 字节码(即扩展名为 .class 的文 ...
- C++primer学习笔记(三)——Chapter 5
5.1 Simple Statements 1.记得每个语句后面加上”;”不过现在编译器都有实时编译,一般都不会忘记的, 2.空语句 (1)就是啥都没有.只有一个“:” (2)还是有很多用处的,例 ...
- Scala学习笔记(三)类层级和特质
无参方法 功能:将方法的定义转换为属性字段的定义: 作用范围:方法中没有参数,并且方法仅能通过读取所包含的对象属性去访问可变状态,而不改变可变状态,就可使用无参方法: 例子: abstract cla ...
- C++Primer第5版学习笔记(三)
C++Primer第5版学习笔记(三) 第四/五章的重难点内容 你可以点击这里回顾第三章内容 因为第五章的内容比较少,因此和第四章的笔记内容合并. 第四章是 ...
- C#可扩展编程之MEF学习笔记(三):导出类的方法和属性
前面说完了导入和导出的几种方法,如果大家细心的话会注意到前面我们导出的都是类,那么方法和属性能不能导出呢???答案是肯定的,下面就来说下MEF是如何导出方法和属性的. 还是前面的代码,第二篇中已经提供 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- PyQt4入门学习笔记(三)
# PyQt4入门学习笔记(三) PyQt4内的布局 布局方式是我们控制我们的GUI页面内各个控件的排放位置的.我们可以通过两种基本方式来控制: 1.绝对位置 2.layout类 绝对位置 这种方式要 ...
随机推荐
- Keras学习~试用卷积~跑CIFAR-10
import numpy as np import cPickle import keras as ks from keras.layers import Dense, Activation, Fla ...
- Nop源码分析二
上文我们已经通过该行代码:var typeFinder = containerManager.Resolve<ITypeFinder>(); 从注入容器中获取到了typeFinder实例. ...
- vim如何配置go语言环境
go语言没有如source insight般优秀的编辑器,试用了多种,vim算最好的,其次可以用liteide(有反查变量函数引用点.修改行变色功能),两者可配合使用. 更新:最好的是idea+go插 ...
- PL/SQL Developer主数据库连接和窗口连接切换
Oracle开发者估计对PL/SQL Developer都非常熟悉了,里面有些小的功能点大概还有些初学者没发现.PL/SQL Developer支持多连接多窗口,下面详细说说. 主连接的概念 打开PL ...
- IE9浏览器中的My97日历控件刷新后无法打开问题解决办法
解决办法如下: 1:找到WdatePicker.js 2:将$crossFrame:true 改为$crossFrame:false. 3:重启服务,刷新页面发现OK.
- HTML 字符图案
Dog: <!-- :: :;J7, :, ::;7: ,ivYi, , ;LLLFS: :iv7Yi :7ri;j5PL ,:ivYLvr ,ivrrirrY2X, :;r@Wwz.7r: : ...
- “You couldn’t see my tears cause I am in the water.“ Fish said to water.“But I could feel your tears cause you are in my heart..“ Answered water.
“You couldn’t see my tears cause I am in the water.“ Fish said to water.“But I could feel your tears ...
- 计算缓存文件大小、清除缓存的Cell
计算缓存文件大小 - (void)getCacheSize { // 总大小 unsigned long long size = 0; // 获得缓存文件夹路径 NSString *cachesPat ...
- Bash:-3次错误输入退出脚本
Limit_Condition() { let count++ ]];then echo "超过3次机会,自动关停脚本" exit fi Comfirm() { count= wh ...
- 利用JavaScript来实现省份—市县的二级联动
所谓省-市二级联动是指当选择省份下拉选择框时,市县的下拉框会根据选择的省市而有相应的市县加载出来,如下图所示选择"上海市",城市的下拉选择框只会出现上海的市县: 这种二级联动非常常 ...