第1章:C++泛型技术基础:模板——《C++泛型:STL原理和应用》读书笔记整理
第1章:C++泛型技术基础:模板
1.2 关于模板参数
1.2.1 模板参数类型
类型参数
typename声明的参数都属于类型参数,它的实参必须为系统内置或者用户自定义的数据类型,包括类模板实体,由类模板产生的类模板实体,本质上就是类。非类型参数
C++允许人们在模板参数列表中像函数参数列表中那样定义普通变量或者对象。定义的普通变量不能被修改,因为模板参数是在预编译期间进行传递并且被编译的。仅支持可以转换为Int类型的变量(double都不行!)、枚举、指针、引用。template<typename T, int b>
模板定义型参数
C++也允许以类模板的定义作为类模板参数,之所以需要这种参数,其目的就是除了强调这个参数的实参必须为类模板外,还强调这个类模板所具有的参数个数。//其中T就是一个单参数类模板
template<template<typename S>class T>
1.2.2 模板形参和实参的结合
- 函数模板实参的隐式提供
实参的隐式提供需要提供返回值的类型,或者可以使用auto+decltype大法推导返回值的类型。 - 指针实参
指针其实是类型参数的一种,使用4字节的存储空间。 - 修饰符const和&的使用
修饰符const和&只是对模板参数的修饰,对模板参数类型无太大影响
1.3 特化模板和模板具现规则
1.3.1 特化模板(特例化模板)
- 函数模板中的特化模板
没啥好说的就是普通的特化。不过模板函数也是可以偏特化的,比如一个双参数函数,是可以特化第一个参数的。 - 类模板的特化和偏特化
偏特化就是指特化一个模板参数。 - 模板的具现
在源文件中,我们可以使用多种方式编写一个泛型的程序功能模块,他可能是特化模板、偏特化模板以及全特化模板,或者这几种泛型模块共存。显然编译器需要一个模板具现的规则,优先生成哪个模板的实体。编译器对于一个模块调用的具现优先规则应为:
特化模板 > 偏特化模板 > 普通模板 > 系统
1.4 右值引用与模板
1.4.1 右值引用
左值值既可以出现在赋值运算符的左边和右边,右值只可以出现在赋值运算法的右边。左值之所以可以出现在赋值运算符的左边,就是因为这种表达式代表一块存储空间,可以接受并保存数据。程序可以通过其变量名获取地址,并用这个地址访问数据。右值仅能代表数据。右值表达式要么是数据本身,要么是一个能得出结果的运算表达式,尽管它也占据一定的存储空间,但是因为它没有名字,也不能从其表达式中提取这个空间的地址。因此这种表达式只能出现在赋值运算符的右边,而且仅能代表生命期与其所在语句相同的临时对象。
关于引用,在C++11之前,左值可以定义两种引用:左值常引用和左值引用。对于右值,C++11之前仅定义了一种常量引用。对右值进行常量引用可以延长右值的生命期,从而使后续程序可以利用它的信息,遗憾的就是它是常量,不能满足程序更多的要求。
为了能充分利用临时对象,C++11标准推出了一种新的数据类型——右值的非常量引用,简称右值引用。
T&& name = rvalue;
1.4.2 右值引用应用1——转移语义
深拷贝与浅拷贝:
#include <iostream>
using namespace std;
class Student {
private:
int num;
char *name;
public:
Student();
~Student();
};
Student::Student(){
name = new char(20);
cout << "Student" << endl;
}
Student::~Student(){
cout << "~Student " << (int)name << endl;
delete name;
name = NULL;
}
int main()
{
Student s1;
// 使用了默认拷贝函数,默认拷贝函数进行的是浅拷贝
// 因此在析构的时候会对同一个内存空间释放两次,造成内存泄漏。
Student s2(s1);
return 0;
}
浅拷贝优点是速度快,节省资源,缺点是共享了资源,容易引起内存泄漏。深拷贝不会引起泄漏,但是每次拷贝都消耗大量资源。
转移语义:
//关闭RVO,return value optimistic,返回值优化
//不关闭在返回右值的时候会跳过移动构造函数,直接构造对象。
g++ -fno-elide-constructors e1_b.cpp -o e1_b && ./e1_b
Foo fuct(){
Foo foof(100); //产生局部变量,生命周期只有在fuct()函数中,在函数返回的时候会被析构。
return foof; //这里其实是调用了Foo类的拷贝函数,拷贝了一份作为返回值。
}
fuct();
Foo(Foo&& r) //调用拷贝函数,返回一份拷贝
~Foo() //函数中的局部变量生命周期结束
~Foo() //函数返回值的生命周期结束,在主函数return的时候
//为什么会调用了一次析构函数呢?
//因为foo1的生命周期是整个主函数,少的那一次在主函数return的时候会被调用
//不是初始化!没有调用构造函数!声明了一个常引用接受fuct()的返回值。
const Foo& foo1 = fuct();
Foo(Foo&& r) //拷贝局部变量作为返回值
~Foo() //局部变量声明周期结束
//与上相同,只是一个是常引用,另一个是非常量引用
Foo&& foo2 = fuct();
Foo(Foo&& r)
~Foo()
//隐式调用构造函数和显式调用构造函数!
Foo foo3(fuct());
Foo foo3 = Foo(fuct());
Foo(Foo&& r) //拷贝作为返回
~Foo() //析构局部变量
Foo(Foo&& r) //调用拷贝构造函数,创建对象foo3,
~Foo() //返回值声明周期结束,调用析构函数
return 0!
在某些情况下,被拷贝的对象是右值,意味着其生命周期即将结束,此时如果我们再去消耗资源开辟新空间就显得浪费了。因为被拷贝对象即将”死亡“,我们不妨借用一下这个右值的内存空间!这就是语义转移,即使用浅拷贝共享内存空间后,将”强迫“原对象放弃资源控制权(指针为空),避免内存空间被释放。通过这种方式,原对象(右值)的内存空间,被我们转移(窃取)出来,用于新对象,通过这种方式,我们极大地节省了开销。
1.4.3 右值引用应用2——转移函数move()
看到了右值引用的好处,左值引用也想利益共沾。但是右值引用,必须参数要是右值才能完成语义转移,因为左值在逻辑上是不允许被窃取内存空间的。
T&& std::move(T&);
void swap(T& a, T& b){
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
1.4.4 右值引用应用3——参数完美转发模板
在程序设计实践中,函数模板经常需要调用一些外部函数实现它的功能,这时候就需要模板为这些函数传递参数,人们习惯上把模板的这种行为叫做参数转发。对于模板来说,它的任务就是参数的忠实转发,一不能改变参数特性,二不能产生额外开销。如果模板能把所有数据类型都按照上述要求进行转发,那么这个模板就是一个完美参数转发者。
//方案一:
void Func(int); //目标函数
template<typename T>
Tmp(T); //转发模板
//结果:这种方式在Tmp(10),参数为右值的时候,在经模板转发后变成了左值
//方案二:
void Func(int v);
template<typename T>
Tmp(T&);
//结果:无法转发右值,Tmp(100)报错,因为模板为左值引用
//改进,Tmp(const T&),此时模板转发后参数变为常引用,可能不符合某些函数要求
//方案三:
void Func(int& v);
template<typename T>
Tmp(T&& a){
Func(a);
}
//结果:C++11前无法通过,因为C++11前还没有右值引用T&&,以及对多个引用符&连用的数据类型进行推导的功能
/* 多重引用推导表
实参类型 模板参数 推导类型
int& T& int&
int&& T& int&
int& T&& int&
int&& T&& int&&
lvalue T&& T& 左值 + T&& = T&
rvalue T&& T 右值 + T&& = T(左值)
*/
//在这种推导情况下,如果Tmp的参数为右值,在转发后参数类型就会被推导为左值,这与我们的预期不同。
//对此我们在Tmp函数内对参数进行强制的类型转换
Tmp(T&& a){
Func(static_cast<T&&>(a));
}
//因为左值+T&& = T&&, T& + T&& = T&, 实现了参数的完美转发。
//为了区别于move()和static_cast,并使之更具有语义性,C++11将static_cast()封装在函数模板std:forward()。
Tmp(T&& a){
func(std::forward<T>(a));
}
第1章:C++泛型技术基础:模板——《C++泛型:STL原理和应用》读书笔记整理的更多相关文章
- chap1 C++泛型技术基础--模板 #STL
0 缘起 有一点编程经验和积累,想系统的学习下STL,以前都是随意做的笔记,现在想着成主题的输出一下. 书的原型是ISBN:9787302421757 <C++泛型STL原理和应用>,是从 ...
- 《黑客攻防技术宝典Web实战篇@第2版》读书笔记1:了解Web应用程序
读书笔记第一部分对应原书的第一章,主要介绍了Web应用程序的发展,功能,安全状况. Web应用程序的发展历程 早期的万维网仅由Web站点构成,只是包含静态文档的信息库,随后人们发明了Web浏览器用来检 ...
- 第2章:C++泛型机制的基石:数据类型表——《C++泛型:STL原理和应用》读书笔记整理
第二章:C++泛型机制的基石--数据类型表 2.1 类模板的公有数据类型成员 2.1.1 类的数据类型成员 C++类中不仅可以定义数据成员和函数成员,而且还可以定义数据类型成员.在泛型设计中,类的 ...
- STL源码剖析读书笔记--第四章--序列式容器
1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...
- STL源码分析读书笔记--第二章--空间配置器(allocator)
声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...
- 《KVM虚拟化技术实战和原理解析》读书笔记(十几篇)
第一章和第二章 第一章 虚拟化和云计算 Saas(软件即服务):将已经部署好的软件作为一种服务来提供,比如:Google Docs, Google Apps Paas(平台即服务):将开发环境作为一种 ...
- STL源码分析读书笔记--第三章--迭代器(iterator)概念与traits编程技法
1.准备知识 typename用法 用法1:等效于模板编程中的class 用法2:用于显式地告诉编译器接下来的名称是类型名,对于这个区分,下面的参考链接中说得好,如果编译器不知道 T::bar 是类型 ...
- Windows环境下多线程编程原理与应用读书笔记(2)————面向对象技术
面向对象技术是学C++需要重点掌握的知识,因为我觉得自己的基础还是比较可以,这一章节的内容就只是粗略的读了一遍,在此就不做过多的笔记.
- C#夯实基础之接口(《CLR via C#》读书笔记)
一. 接口的类型 接口是引用类型.因此从值类型赋值给接口是需要装箱的.如下所示: class Program { static void Main(string[] args) { ISay catS ...
随机推荐
- 【Android - 控件】之MD - NavigationView的使用
NavigationView是Android 5.0新特性——Material Design中的一个布局控件,可以结合DrawerLayout使用,让侧滑菜单变得更加美观(可以添加头部布局). Nav ...
- docker配置mysql主从与django实现读写分离
一.搭建主从mysql环境 1 下载mysql镜像 docker pull mysql:5.7 2 运行刚下载的mysql镜像文件 # 运行该命令之前可以使用`docker images`是否下载成功 ...
- PHP自动发红包代码示例
<?php header('Content-type:text'); define("TOKEN", "weixin"); $wechatObj = ne ...
- PHP fsockopen受服务器KeepAlive影响的解决
在开发过程中常常遇到这样的需求,模拟浏览器访问某接口,并获取返回数据.我们比较常使用的方法是fsockopen与接口建立连接,然后发出指令,然后通过fgets接受返回值. 但是我们发现,通过PHP模拟 ...
- 使用curl创建简单的性能监控工具
cURL,全称Command Line URL viewer,是一种命令行工具,用来发送网络请求,然后得到和提取数据,显示在标准输出(stdout). 我们可以使用curl来获取网页的源码,显示头信息 ...
- 为什么说 Java 中只有值传递?
对于初学者来说,要想把这个问题回答正确,是比较难的.在第二天整理答案的时候,我发现我竟然无法通过简单的语言把这个事情描述的很容易理解,遗憾的是,我也没有在网上找到哪篇文章可以把这个事情讲解的通俗易懂. ...
- luogu P3572 [POI2014]PTA-Little Bird
题目描述 从1开始,跳到比当前矮的不消耗体力,否则消耗一点体力,每次询问有一个步伐限制,求每次最少耗费多少体力 单调队列优化动态规划 #include<cstdio> #include&l ...
- Multiplication Game
Description Alice and Bob are in their class doing drills on multiplication and division. They quick ...
- python 金融应用(一)期权定价公式的计算
一.基于不付息的欧式期权看涨BSM公式 假定股票服从下列微分方程: 期权定价公式: 二.蒙特卡洛模拟 import numpy as np import math from time import t ...
- 基于cyusb3014的usb3.0双目摄像头开发测试小结(使用mt9m001c12stm)
测试图像 摄像头分辨率为1280*1024,双目分辨率为2560*1024 ps:时钟频率太高,时序约束还得进一步细化,图像偶尔会出现部分雪花,下一步完善