注:本文仅作个人做笔记复习用,不具有教学意义!
目录
Traits2:如果一个类模板中,全部的成员都是公有数据类型,这个模板就是一个独立的数据类型表,用来规范数据
模板的定义
- 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属
- 模板用于表达逻辑结构相同,但具体数据类型元素不同的数据
- 对象的通用行为
函数模板和模板函数
#include <iostream>
using namespace std;
//函数模板
template<typename T>
void myswap(T &a,T &b) {
T temp;
temp = a;
a = b;
b = temp;
}
int main() {
int a = 1, b = 2;
//模板函数,编译器根据传入参数生成的模板特例
myswap(a, b);
cout << a << " " << b << endl;
double c = 2.2, d = 3.3;
//模板函数,编译器根据传入参数生成的模板特例
myswap(c, d);
cout << c << " " << d << endl;
return 0;
}
函数模板不提供隐式类型转换
template<typename T>
void fun(T& a,T& b) {
cout << "template被调用" << endl;
}
void fun(char c,int y) {
cout << "普通函数被调用" << endl;
}
int main() {
char ch = 'a';
int data = 23;
fun<int>(ch, data);//❌,函数模板不提供隐式类型转化,必须严格遵守T的类型定义
fun(ch, data);//✔,调用普通函数
return 0;
}
当函数模板和普通函数都符合调用规则时,优先使用普通函数
- 因为普通函数在编译的期间就生成了函数体
- 而模板函数的生成在调用的时候,编译器才会编译
template<typename T>
void fun(T& a,T& b) {
cout << "template被调用" << endl;
}
void fun(int &a,int &b) {
cout << "普通函数被调用" << endl;
}
int main() {
int data1 = 32;
int data2 = 23;
fun<int>(data1, data2);//template被调用
fun(data1, data2);//普通函数被调用
return 0;
}
编译器在处理函数模板的时候能够生成任意类型的函数
根据调用的实际产生不同的函数:
编译器会对函数模板进行二次编译:这是参数化的基础,也是成为编译时多态的由来。
在声明的地方对模板代码本身进行编译,在调用的地方对参数化以后的具体调用进行编译
hpp:h+cpp的结合体,声明与实现结合
【C++】模板声明与定义不分离_Yngz_Miao的博客-CSDN博客_c++模板声明
类模板
- 类模板用于实现类所需数据的类型参数化
- 类模板在表示如数组、表、图等数据结构显得特别重要
- 这些数据结构的表示和算法不受所包含的元素类型影响
#include <iostream>
using namespace std;
template<class T1,class T2>
class MyClass {
public:
T1 _t1;
T2 _t2;
MyClass(T1 t1, T2 t2) :_t1(t1), _t2(t2) {}
void print() {
cout << _t1 << "," << _t2 << endl;
}
};
int main() {
MyClass<int, int>mc(1, 2);
mc.print();
return 0;
}
类模板应用:Array容器的仿真复现
MyArray.hpp:
#pragma once
template<class T,int n>
class MyArray {
public:
MyArray();
MyArray(int length);
~MyArray();
int size();
T get(int num);
T& operator[](int num);
void set(T data, int num);
public:
T* pt;
};
template<class T, int n>
inline MyArray<T, n>::MyArray()
{
this->pt = new T[n];
}
template<class T, int n>
inline MyArray<T, n>::MyArray(int length)
{
this->pt = new T[length];
}
template<class T, int n>
inline MyArray<T, n>::~MyArray()
{
delete[]this->pt;
}
template<class T, int n>
inline int MyArray<T, n>::size()
{
return n;
}
template<class T, int n>
inline T MyArray<T, n>::get(int num)
{
if (num >= n || num < 0) {
//exception;
}
else {
return *(this->pt + num);
}
}
template<class T, int n>
inline T& MyArray<T, n>::operator[](int num)
{
if (num >= n || num < 0) {
}
else {
return *(pt + num);
}
}
template<class T, int n>
inline void MyArray<T, n>::set(T data, int num)
{
if (num >= n || num < 0) {
//
}
else {
*(pt + num) = data;
}
}
MyArray.cpp
#include <iostream>
#include "MyArray.hpp"
using namespace std;
int main() {
MyArray<int, 5>arr;
for (int i = 0; i < arr.size(); i++) {
arr.set(i * 10, i);
cout << arr[i] << endl;
}
return 0;
}
模板类继承:
模板类继承模板类:
#include <iostream>
#include <string>
using namespace std;
template<class T>
class MyClass {
public:
T x;
MyClass(T t) :x(t) {}
virtual void print() = 0;
};
template<class T>
class NewClass :public MyClass<T> {
public:
T y;
NewClass(T t1, T t2) :MyClass(t1), y(t2) {}
void print() {
cout << "x=" << x << " y=" << y << endl;
}
};
int main() {
MyClass<int>* p = new NewClass<int>(10, 8);
p->print();
return 0;
}
模板类继承普通类:
class XYZ {
public:
int x, y, z;
XYZ(int a, int b, int c) :x(a), y(b), z(c) {}
virtual void print() {
cout << x << " " << y << " " << z << " " << endl;
}
};
template<class T>
class GoodsXYZ :public XYZ {
public:
int t;
GoodsXYZ(int t1, int a, int b, int c) :XYZ(a, b, c), t(t1) {}
void print() {
cout << "T 类型的值" << t << endl;
cout << x << " " << y << " " << z << endl;
}
};
普通类继承模板类
class RunClass :public GoodsXYZ<int> {
public:
int dd = 1000;
RunClass(int a2, int b2, int c2, int d2) :GoodsXYZ<int>(a2, b2, c2, d2) {}
void print() {
cout << dd << x << y << z;
}
};
模板类的嵌套
#include <iostream>
using namespace std;
template<class T>
class MyTestNestClass {
public:
class nClass {
public:
int num;
};
nClass nObj1, nObj2;
template<class V>
class RunClass {
public:
V v1;
};//嵌套类模板,不能直接初始化
RunClass<T>t1;
RunClass<double>t2;
};
int main() {
MyTestNestClass<int>myObj1;
myObj1.nObj1.num = 10;
myObj1.t1.v1 = 13;
myObj1.t2.v1 = 6.18;
cout << myObj1.nObj1.num<< endl;
cout << myObj1.t1.v1 << endl;
cout << myObj1.t2.v1 << endl;
return 0;
}
左值与右值:
int a = 10;
int b = 20;
int c = a + b;
__asm {
mov eax,a
mov ebx,b
add eax,ebx
mov c,eax
}
左值与右值简介
左值:
- 可以出现在赋值运算符左边
- 往往代表一个存储空间
- 左值是一个有名字,有固定地址的表达式
右值:
- 所谓的数据
- 由于计算式涉及到了计算机空间,仅仅在表达式运行过程中存在
- 右值是一个与运算过程相匹配的临时对象,在对应语句执行完毕之后就销毁了
- 所以无法从语法上取到右值地址
- 右值仅仅是一个匿名,没有固定地址的对象
右值引用:使得右值变成一个与左值完全相同的持久对象
int x = 10, y = 20;
int&& right = x + y;
如果有一个临时对象,那么使用浅拷贝会有巨大的意义
当我们需要具有”转移语意“的拷贝构造函数时,浅拷贝的意义就凸显了。
#include <iostream>
using namespace std;
class Foo {
public:
Foo(int x) {
}
Foo(const Foo& r) {
//this->p = r.p;//浅拷贝,p和r.p指向了同一个地址
p = new int;
*p = *(r.p);//深拷贝
}
Foo(Foo&& r) {
cout << "Foo(Foo&&)" << endl;
p = r.p;//p和r.p指向了同一个地址
r.p = nullptr;//源对象r放弃资源所有权
}
private:
int* p;
};
Foo func() {
Foo foo(100);
return foo;
}
int main() {
Foo f(func());//资源所有权发生转移,资源位置没有改变而所属对象变化
return 0;
}
转移函数和完美转发模板
#include <iostream>
using namespace std;
void Func(int& x) {
cout << "左值引用" << endl;
}
void Func(int&& x) {
cout << "右值引用" << endl;
}
void Func(const int& x) {
cout << "左值常引用" << endl;
}
void Func(const int&& x) {
cout << "右值常引用" << endl;
}
template<typename T>
void FuncT(T&& a) {
Func(std::forward<T>(a));//使用std::forward进行类型推导
}
int main() {
FuncT(10);
int a;
FuncT(a);
FuncT(std::move(a));
const int b = 10;
FuncT(b);
FuncT(std::move(b));
return 0;
}
STL模板技术
C++泛型机制的基石-数据类型表
Traits1:利用typedef:类型萃取器
实现内嵌数据类型->从模板传入类型
template<typename T>
struct map {
typedef T value_type;
typedef T& reference;
typedef T* pointer;
};
template<typename T,typename U>
class A :public TypeTb1<T, U> {
};
int main() {
map<int>::value_type a = 100;
map<int>::reference b = a;
map<int>::pointer c = &a;
return 0;
}
Traits2:如果一个类模板中,全部的成员都是公有数据类型,这个模板就是一个独立的数据类型表,用来规范数据
①定义一个规范类模板类型的基类模板
②凡是继承了这个类型表的模板,它的访问类型就被确定
STL库中,设计人员经常使用这种技巧:
template<class _A1m,class _A2, class R>
class binary
{
typedef _A1 Arg1;//第一个形参类型
typedef _A2 Arg2;//第二个形参类型
typedef R Rtn;//返回值类型
};
举例:
template<typename TR,typename T1,typename T2>
class Add :public binary<TR, T1, T2> {
public:
TR bFunction(const T1& x, const T2& y)const {
return x + y;
}
};
int main() {
double a = 100.01, b = 20.2;
Add<double, double, double>addObj;
cout << addObj.bFunction(a, b) << endl;
return 0;
}
因为Add包含了binary的类型数据表,因此系统中的其他模块就可以使用Add::Arg1,Add::Arg2,Add::Rtn这种方式和Add本身进行对接。
这种数据类型的抽象,达到了多个系统模块之间的类型统一。
Traits3:非侵入式STL类型设计与数据类型
#include <iostream>
using namespace std;
class Test1;
class Test2;
//两个类模板规范一个统一的接口
template <typename T>
class TypeTb1 {
};
//特化模板1
template<>
class TypeTb1<Test1> {
public:
typedef char ret_type;
typedef int par1_type;
typedef double par2_type;
};
//特化模板2
template<>
class TypeTb1<Test2> {
public:
typedef double ret_type;
typedef double par1_type;
typedef int par2_type;
};
template<typename T>
class Test {
public:
typename TypeTb1<T>::ret_type compute
(
typename TypeTb1<T>::par1_type x,
typename TypeTb1<T>::par2_type y
)
{
return x;
}
};
int main() {
Test<Test1>t1;
cout << t1.compute(65, 6.18) << endl;
return 0;
}
Traits4:Traits的原理及应用:Iterator
template<typename T>
struct Traits{
};
template<typename T>
struct Traits<T*> {
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
};
int main() {
Traits<double*>::value_type t2 = 4.44;
cout << t2 << endl;
return 0;
}
shared_ptr的仿真实现Shared_ptr:
#include<iostream>
using namespace std;
template<typename T>
class Shared_ptr;
template<typename T>
class Res_ptr {
private:
T* res_p;
int use_num;
Res_ptr(T* p) :res_p(p), use_num(1) {
cout << "res 构造函数" << endl;
}
~Res_ptr() {
cout << "res 析构函数" << endl;
}
friend class Shared_ptr<T>;
};
template<typename T>
class Shared_ptr {
public:
Shared_ptr(T* p) :ptr(new Res_ptr<T>(p)) {
cout << "Shared_ptr的构造函数 " << " use_num=" << ptr->use_num << endl;
}
Shared_ptr(const Shared_ptr& origin) :ptr(origin.ptr) {
++ptr->use_num;
cout << "Shared_ptr的拷贝构造函数" << " use_num=" << ptr->use_num << endl;
}
~Shared_ptr() {
cout<<"Shared_ptr的析构函数"<< " use_num=" << ptr->use_num << endl;
if (--ptr->use_num == 0) {
delete ptr;
}
}
private:
Res_ptr<T>* ptr;//指向计数类Res_ptr
};
int main() {
{
Shared_ptr<int>hpA = Shared_ptr<int>(new int(42));
{
Shared_ptr<int>hpB(hpA);
Shared_ptr<int>hpC(hpB);
Shared_ptr<int>hpD = hpA;
}
cout << "内层括号结束!" << endl;
}
cout << "中层括号结束!" << endl;
return 0;
}