C++模板与STL(一):模板基础与STL仿真复现

注:本文仅作个人做笔记复习用,不具有教学意义! 

目录

模板的定义

函数模板和模板函数

函数模板不提供隐式类型转换 

当函数模板和普通函数都符合调用规则时,优先使用普通函数

hpp:h+cpp的结合体,声明与实现结合

 类模板

类模板应用:Array容器的仿真复现

模板类继承:

模板类的嵌套

左值与右值简介 

转移函数和完美转发模板

STL模板技术

C++泛型机制的基石-数据类型表

Traits1:利用typedef:类型萃取器

Traits2:如果一个类模板中,全部的成员都是公有数据类型,这个模板就是一个独立的数据类型表,用来规范数据

Traits3:非侵入式STL类型设计与数据类型

Traits4:Traits的原理及应用:Iterator

shared_ptr的仿真实现Shared_ptr:


模板的定义

  • 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属
  • 模板用于表达逻辑结构相同,但具体数据类型元素不同的数据
  • 对象的通用行为

函数模板和模板函数

#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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值