第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 ...
随机推荐
- win10 安装 MySQL-5.7.28 记录
目录 一.安装前准备 二.安装步骤 三.安装时踩的坑 一.安装前准备 1.云盘下载安装包以及客户端工具 下载地址:MySQL-5.7.28 + SQLyog 2.官网下载安装包 下载地址:https: ...
- java调用webservice,比较简单方便的方法。
首先,请同学们自行了解webservice的基础知识. 个人理解,webservice约等于使用http+xml技术进行跨平台的数据交互. http和xml我们都很熟悉了,把他们两个组合到一起就是we ...
- Spring Boot中@ConditionalOnProperty使用详解
在Spring Boot的自动配置中经常看到@ConditionalOnProperty注解的使用,本篇文章带大家来了解一下该注解的功能. Spring Boot中的使用 在Spring Boot的源 ...
- 鲲鹏性能优化十板斧(二)——CPU与内存子系统性能调优
1.1 CPU与内存子系统性能调优简介 调优思路 性能优化的思路如下: l 如果CPU的利用率不高,说明资源没有充分利用,可以通过工具(如strace)查看应用程序阻塞在哪里,一般为磁盘,网络或应 ...
- 转:ETL讲解(很详细!!!)
ETL讲解(很详细!!!) ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程,目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据. ETL是BI项目重要 ...
- 第五章 初始jQuery
jQuery与JavaScript: jQuery的用途: 访问和操作DOM元素: 控制页面样式: 对页面事件的处理: 方便地使用jQuery插件: 与Ajax技术的完美结合: jQuery的优势: ...
- ThreadLocal解决了什么问题
小明所在的项目组(迭代组:一直在迭代的路上),经常会在已有接口的基础上开发一些小功能,并且前提是在保证现有用户的不受影响基础上迭代.功能迭代,在代码层面小明有1w种实现方法(吹牛的),一起来看看这次小 ...
- 【HTTP】HTTP 的15个常见知识点复习
前言 自从入职新公司到现在,我们前端团队内部一直在做 ?每周一练 的知识复习计划,我之前整理了一个 每周一练 之 数据结构与算法 学习内容,大家也快去看看~~ 最近三周,主要复习 网络基础 相关的知识 ...
- inline以及inline-block行内元素:vertical-align属性
- synchronized和volatile区别
不同一: synchronized可以修饰方法, volatile只能修饰变量 不同二: synchronized是同步的 volatile修饰的变量具有可见性.