第1章:C++泛型技术基础:模板

1.2 关于模板参数
1.2.1 模板参数类型
  1. 类型参数

      typename声明的参数都属于类型参数,它的实参必须为系统内置或者用户自定义的数据类型,包括类模板实体,由类模板产生的类模板实体,本质上就是类。

  2. 非类型参数

      C++允许人们在模板参数列表中像函数参数列表中那样定义普通变量或者对象。定义的普通变量不能被修改,因为模板参数是在预编译期间进行传递并且被编译的。仅支持可以转换为Int类型的变量(double都不行!)、枚举、指针、引用。

     template<typename T, int b>
  3. 模板定义型参数

      C++也允许以类模板的定义作为类模板参数,之所以需要这种参数,其目的就是除了强调这个参数的实参必须为类模板外,还强调这个类模板所具有的参数个数。

     //其中T就是一个单参数类模板
    template<template<typename S>class T>
1.2.2 模板形参和实参的结合
  1. 函数模板实参的隐式提供

      实参的隐式提供需要提供返回值的类型,或者可以使用auto+decltype大法推导返回值的类型。
  2. 指针实参

      指针其实是类型参数的一种,使用4字节的存储空间。
  3. 修饰符const和&的使用

      修饰符const和&只是对模板参数的修饰,对模板参数类型无太大影响
1.3 特化模板和模板具现规则
1.3.1 特化模板(特例化模板)
  1. 函数模板中的特化模板

      没啥好说的就是普通的特化。不过模板函数也是可以偏特化的,比如一个双参数函数,是可以特化第一个参数的。
  2. 类模板的特化和偏特化

      偏特化就是指特化一个模板参数。
  3. 模板的具现

      在源文件中,我们可以使用多种方式编写一个泛型的程序功能模块,他可能是特化模板、偏特化模板以及全特化模板,或者这几种泛型模块共存。显然编译器需要一个模板具现的规则,优先生成哪个模板的实体。编译器对于一个模块调用的具现优先规则应为:

特化模板 > 偏特化模板 > 普通模板 > 系统

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原理和应用》读书笔记整理的更多相关文章

  1. chap1 C++泛型技术基础--模板 #STL

    0 缘起 有一点编程经验和积累,想系统的学习下STL,以前都是随意做的笔记,现在想着成主题的输出一下. 书的原型是ISBN:9787302421757 <C++泛型STL原理和应用>,是从 ...

  2. 《黑客攻防技术宝典Web实战篇@第2版》读书笔记1:了解Web应用程序

    读书笔记第一部分对应原书的第一章,主要介绍了Web应用程序的发展,功能,安全状况. Web应用程序的发展历程 早期的万维网仅由Web站点构成,只是包含静态文档的信息库,随后人们发明了Web浏览器用来检 ...

  3. 第2章:C++泛型机制的基石:数据类型表——《C++泛型:STL原理和应用》读书笔记整理

    第二章:C++泛型机制的基石--数据类型表 2.1 类模板的公有数据类型成员 2.1.1 类的数据类型成员   C++类中不仅可以定义数据成员和函数成员,而且还可以定义数据类型成员.在泛型设计中,类的 ...

  4. STL源码剖析读书笔记--第四章--序列式容器

    1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...

  5. STL源码分析读书笔记--第二章--空间配置器(allocator)

    声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...

  6. 《KVM虚拟化技术实战和原理解析》读书笔记(十几篇)

    第一章和第二章 第一章 虚拟化和云计算 Saas(软件即服务):将已经部署好的软件作为一种服务来提供,比如:Google Docs, Google Apps Paas(平台即服务):将开发环境作为一种 ...

  7. STL源码分析读书笔记--第三章--迭代器(iterator)概念与traits编程技法

    1.准备知识 typename用法 用法1:等效于模板编程中的class 用法2:用于显式地告诉编译器接下来的名称是类型名,对于这个区分,下面的参考链接中说得好,如果编译器不知道 T::bar 是类型 ...

  8. Windows环境下多线程编程原理与应用读书笔记(2)————面向对象技术

    面向对象技术是学C++需要重点掌握的知识,因为我觉得自己的基础还是比较可以,这一章节的内容就只是粗略的读了一遍,在此就不做过多的笔记.

  9. C#夯实基础之接口(《CLR via C#》读书笔记)

    一. 接口的类型 接口是引用类型.因此从值类型赋值给接口是需要装箱的.如下所示: class Program { static void Main(string[] args) { ISay catS ...

随机推荐

  1. 成功的面对对象语言的五个基本特性——Alan Kay

    1.万物皆为对象. 将对象是为奇特的变量,它可以存储数据,除此之外,还可以要求他在自身上执行操作. 2.程序是对象的集合,他们通过发送消息来告知彼此所要做的. 要想请求一个对象,就必须对该对项发送一条 ...

  2. udp协议以及socketserver

    udb协议 udb协议也是一种协议,它和tcp相比既有缺点也有优点 udb协议所发送的数据可以理解为自带报头,所以他不会出现粘包的情况.但是udp数据只管发送而不管接收,也就是说udp会出现丢包的情况 ...

  3. java之--加密、解密算法

    0.概述 在项目开发中,我们常需要用到加解密算法,加解密算法主要分为三大类: 1.对称加密算法,如:AES.DES.3DES 2.非对称加密算法,如:RSA.DSA.ECC 3.散列算法,如:MD5. ...

  4. springcloud-Eureka组件

    一.简介 Eureka 是 Netflix 开发的服务发现框架,本身是一个基于 REST 的服务,主要用于定位运行在 AWS 域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的.SpringC ...

  5. mysql-常用组件之定时器

    定时器主要用于定时的执行一次或者循环执行一条sql,在实际场景上,例如,定期清理数据表,定期导出日志文件等等场景.本次公司晚上维护系统,晚上需要定期挂维护页,用到了定时器,这里简单总结一下. 启用定时 ...

  6. JsonModel的使用

    本人最近在开发一款医疗类的APP 发现接口返回的数据很复杂 手动解析的话对新手来说就是一场灾难 在分解成所需要的model类型时工作量非常的大,于是从网上查阅相关资料,发现JsonModel这个第三方 ...

  7. Pandas学习(一)——数据的导入

    欢迎加入python学习交流群 667279387 学习笔记汇总 Pandas学习(一)–数据的导入 pandas学习(二)–双色球数据分析 pandas学习(三)–NAB球员薪资分析 pandas学 ...

  8. [TimLinux] Python3 Coverity zeep/SOAP 库使用示例

    废话不多说,上代码: # 基于Coverity的示例 from zeep import Client from zeep.wsse.username import UsernameToken conf ...

  9. POJ 2186 Popular cows(SCC 缩点)

    Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10, ...

  10. CoderForces999F-Cards and Joy

    F. Cards and Joy time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...