《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类 绝对位置 这种方式要 ...
随机推荐
- 解读ContentResolver和ContentProvider
转自:http://cthhqu.blog.51cto.com/7598297/1281217 1. ContentProvider的概述 ContentProvider: (Official Def ...
- JDBC代码模板
import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.ap ...
- (最重要)学了这么久的编程,你知道byte吗?
在c#中,Byte b=12; 上面是合法的,因为Byte是一个字节.他的范围是0-255. 如果是 Byte b=4096;这是错的 ,必须加上强制类型转换. 这个小问题,虽然很小但是值得注意.
- 夺命雷公狗-----React_native---2---sdk的安装
首先回到刚才的那个android的目录下,创建一个sdk文件夹 解压完成后目录结构如下所示: 然后就来设置环境变量,我们需要添加一个"ANDROID_HOME" 然后将这3个文件夹 ...
- Python字符串格式化
一.使用格式化符来格式化字符串: Python支持的所有格式化符 格式化符 意义 'd' 返回要格式化对象的十进制表示,如果可以 'i' 返回要格式化对象的十进制表示,如果可以 'o' 返回要格式化对 ...
- Excel应该这么玩——6、链接:瞬间转移
上一篇中提到通过命名表格来管理基础数据,这样会让数据更规范.如果有很多个基础数据表,需要查找或者修改其中的一个,可以通过名称框中下拉来定位. 但是当表格较多的时候,通过下拉选择的方式就不是很好定位了. ...
- eclipse中搭建svn开发管理环境
1.准备好资源 subversive,若是离线安装,已为大家准备好:http://pan.baidu.com/s/1hrbXH9y(本人喜欢离线安装,在线安装就不在此说了,其实只要一种方法简单的方法能 ...
- css3动画之小牛奔跑
今天突然看到阿里云官网的一个悬浮效果挺炫的,就想知道到底是怎么做的,研究了半天,加了一个技术群,原来是css3做的,然后做了一个小 Demo记录下来: <!DOCTYPE html> &l ...
- Unable to execute dex: GC overhead limit exceeded
Android打包时下面的错误: Unable to execute dex: GC overhead limit exceeded GC overhead limit exceeded 解决的方法: ...
- “error LNK2019: 无法解析的外部符号”之分析
最近在用VS 2008开发,初学遇到不少问题,最头疼的问题之一就是:LNK2019. 百度一下讲的并不够全面,反正都没解决我的问题. error LNK2019问题在VC 6.0中是error LNK ...