《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类 绝对位置 这种方式要 ...
随机推荐
- js事件监听/鼠标滚轮/行为/冒泡/键盘的兼容性写法
addEvent:function(el,type,fn,capture) { if (window.addEventListener) { if (type === "mousewheel ...
- gulp 安装 使用 和删除
1.安装 全局安装: npm intstall gulp -g (首先你得有node.js ,这个可以去node 官网下载个iso的镜像安装包,傻瓜式安装.自带npm) 安装在项目中: 首先 ...
- C与C++中的常用提高程序效率的方法
1.用a++和++a及a+=1代替a=a+1,用a--和--a及a-=1代替a=a-1 通常使用若把一个函数定义为内联函数,则在程序编译阶段,编译器就会把每次调用该函数的地方都直接替换为该函数体中的代 ...
- LCD接口(转载)
LCD接口分类 1. I8080接口,我觉得应该就是所谓的8080,通常会用在12864屏上面,且有内部sdram,不需要实时的刷新图片,速度有限制, 支持的数据宽度有8/9/16/18bit,接 ...
- MATLAB绘图
matlab绘制散点图 clc,clear x=[11.9,11.5,14.5,15.2,15.9,16.3,14.6,12.9,15.8,14.1]; y=[196.84,196.84,197.14 ...
- Material Design Lite,简洁惊艳的前端工具箱 之 交互组件。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接, 博客地址为http://www.cnblogs.com/jasonnode/ . 网站上有对应 ...
- python与ruby的差别
1.引用文件差别 Ruby:同一目录下的文件,如/usr/local/ruby/foo.rb与/usr/local/ruby/bar.rb两个文件.如果直接在foo.rb中 require 'bar' ...
- Azure Management API 之 利用 Windows Azure Management Libraries 来控制Azure platform
在此之前,我曾经发过一篇文章讲叙了如何利用Azure power shell team 提供的class library. 而就在这篇文章发布之后不久,我又发现微软发布了一个preview 版本的Wi ...
- linux 使用 rz 和 sz 命令
linux系统 root权限 lrzsz安装包 ①. 安装 编译安装 root 账号登陆后,依次执行以下命令: tar zxvf lrzsz-.tar.gz cd lrzsz- ./configure ...
- 10 个实用技巧,让 Finder 带你飞
Finder 是 Mac 电脑的系统程序,有的功能类似 Windows 的资源管理器.它是我们打开 Mac 首先见到的「笑脸」,有了它,我们可以组织和使用 Mac 里的几乎所有东西,包括应用程序.文件 ...