知识点总结
1.函数模板-泛型编程,类型参数化:template
,typename和class效果一样 1.T是一个通用的数据类型,告诉编译器如果下面紧跟着的函数或者类中出现T,不要报错
2.模板的使用
1.自动推导类型,编译器必须推导出一致的T数据类型,才能正常使用函数模板。
2.显式指定模板类型:mySwap
(a,b) 3.模板不能单独使用,必须指定出T的类型才可以用
4.类模板的声明和实现不能分文件进行,必须写到一起,和内联函数一样,否则会链接失败。
3.函数模板和普通函数的区别以及调用规则。
1.区别:对于函数模板,编译器进行隐式推导类型的时候,只能识别一致的T类型.
对于普通函数,可以之间进行隐式转换。
2.调用规则:
1.优先调用普通函数,如果想强制调用函数模板,可以使用空模板参数列表:myPrint<>(a,b);
2.如果函数模板能产生更好的匹配,优先使用函数模板
4.函数模板----------->模板函数:过程是由抽象到具体(实例化的过程),在实参进行传递的时候进行类型推导5.函数模板的全特化和偏特化----模板并不是真实通用,对于自定义的数据类型,需要进行特化来使用。
1.模板的全特化:把模板的参数全部特殊化表示出来:template<>,用空模板参数来进行全特化
2.把模板的参数进行一部分特殊化(C++11之后才支持的)
6.函数模板的参数类型:1.类型参数:class/typename T
2.非类型参数:常量表达式,整型:bool/char/short/int/long/size_t。
3.参数类型可以可以设默认值
7.类模板总结
1.类模板的使用只能显式指定类型,可以有默认参数。
2.类模板中的成员函数并不是一开始创建的,而是在运行阶段确定出T的数据类型的时候才去创建
3.类模板做函数的参数:
1.显式指定传入的类型void doWork(Person&p)
2.参数模板化:template
void doWork2(Person&p)
3.整个类模板化:
template
void doWork3(T &p)
4.类模板继承问题:派生类继承基类的时候,必须指定基类中T的类型,
5.类中成员在类外实现的写法
6.友元函数的类外实现.8.C++11可变模板参数---重点难点
1.class... Args或者typename... Args:模板参数包--->class T1, class T2....
2.Args类型后面跟一个省略号:Args... args--->表示0个或者多函数参数,函数参数包
---》T1 t1, T2 t2.....
3.可变参数函数通常是递归的,第一步调用处理包中的第一个实参,然后剩余实参调用自身,为了终止递归,需要定义一个非可变参数的print函数。用来终止递归。
9.实战演练:
1.类模板实现Stack/循环队列Queue
2.单例模式通用模板,可传递任意参数,注意嵌套类成员的初始化
1.可变模板参数例子test01()
1.省略号...位于函数参数args左边的时候,为打包,相当于T1 t1, T2 t2...
2.省略号...位于参数args右边,为拆包
void print()//终止递归的条件
{
cout<//相当于class T1, class T2,......
void print(T t, Args... args)//省略号...位于参数左边的时候,为打包,相当于T1 t1, T2 t2....
{
cout<
运行结果
1
1,2.2,1
1,2.2,1,hello world
test02()
#include
using namespace std;
int sum()//终止递归的条件
{
return 0;
}
template //Args:模板参数包
int sum(T u, Args... args)//args:函数参数包
{
return u+sum(args...);//省略号在左边,拆包
}
void test02()
{
cout<<"sum(1,2,3,4,5,6,7,8,9,10)="<
2.类模板实现通用类型Stack。
#include
using namespace std;
#include
class Person;
template //默认容量设置为10
//默认类型为int
class Stack
{
public:
Stack()
:_top(-1)//栈顶指针
,_data(new T[k_Size]())//默认分配k_Size个空间
{}
bool full()const;//判栈满
bool empty()const;//判栈空
void push(const T &val);//压栈
void pop();//出栈
T top(); //获取栈顶元素
~Stack();
private:
int _top;
T* _data;
};
template
Stack::~Stack()
{
if(_data)
{
delete [] _data;
_data=nullptr;
}
}
template
bool Stack::full()const
{
return _top==k_Size-1;
}
template
bool Stack::empty()const
{
return _top==-1;
}
template
void Stack::push(const T &val)
{
if(!full())
{
_data[++_top] = val;
}
else
{
cout<<"The stack is full"<
void Stack::pop()
{
if(!empty())
{
--_top;
}
else
{
cout<<"The stack is empty()"<
T Stack::top()
{
return _data[_top];
}
void test01()
{
Stack st;
st.push(12);
cout< st;
Person p("孙哲",14);
st.push(p);
cout<
3.函数模板实现队列
#include
#include
using namespace std;
template
class Queue
{
public:
Queue()
:_data(new T[capacity]())//动态数组
,_size(0),_front(0),_rear(0)
{}
void enQueue(const T &val);//入队
void deQueue();//出队
bool empty()const;//队空
bool full()const;//判队满
T get_front()const;//获取队头元素
T get_rear()const;//获取队尾元素
int get_size()const//获取队列长度
{
return _size;
}
~Queue();
private:
int _front;
int _rear;
int _size;
T *_data;
};
template
Queue::~Queue()//析构函数
{
if(_data)
{
delete[]_data;
_data=nullptr;
}
}
template
bool Queue::full()const//判队满
{
return (_rear+1)%capacity == _front;//循环队列
}
template
bool Queue::empty()const
{
return _rear == _front;//队空
}
template
void Queue::enQueue(const T &val)
{
if(!full())
{
_data[_rear] = val;//先入队,再加1
_rear = (_rear+1)%capacity;
++_size;
}
else
{
cout<<"Queue is full"<
void Queue::deQueue()
{
if(!empty())
{
_front = (_front+1)%capacity;//出队
--_size;
}
else
{
cout<<"Queue is empty"<
T Queue::get_front()const//获取队头元素
{
if(!empty())
{
return _data[_front];
}
}
template
T Queue::get_rear()const//获取队尾元素
{
if(!empty())
{
return _data[_rear-1];
}
}
//测试1:int类型数据测试
void test01()
{
Queue myQueue;
myQueue.enQueue(10);
myQueue.enQueue(20);
myQueue.enQueue(30);
myQueue.enQueue(40);
myQueue.enQueue(50);
myQueue.enQueue(60);
myQueue.enQueue(70);
myQueue.enQueue(80);
myQueue.enQueue(90);
myQueue.deQueue();
myQueue.enQueue(100);
while(!myQueue.empty())
{
cout<<"_front="< qPerson;
Person p1("孙悟空",500);
Person p2("猪八戒",400);
Person p3("沙悟净",666);
Person p4("唐 僧",777);
Person p5("白龙马",888);
Person p6("土地老儿",999);
qPerson.enQueue(p1);
qPerson.enQueue(p2);
qPerson.enQueue(p3);
qPerson.enQueue(p4);
qPerson.enQueue(p5);
qPerson.enQueue(p6);
cout<<"Queue size="<#include
using namespace std;
#include
template
class Singleton
{
public:
template //可变模板类型包
static T* getInstance(Args... args)
{
if(nullptr==_pInstance)
{
_pInstance = new T(args...);
atexit(Destroy);//atexit自动释放单例指针
}
return _pInstance;
}
private:
static T* _pInstance;
static void Destroy()
{
if(_pInstance)
{
delete _pInstance;
_pInstance=nullptr;
cout<<"atexit自动释放单例指针"<
T* Singleton::_pInstance = nullptr;//饱汉模式
//Person自定义类
class Person
{
public:
Person(string name,string age)
:_name(name),_age(age)
{
cout<<"Person()"<::getInstance("苏武","28");
ps1->showPerson();
Point *pos1 = Singleton::getInstance(1,3);
pos1->print();
}
int main()
{
test01();
return 0;
}
/*
运行结果:
Person()
苏武 28
Point(int,int)
(1,3)
~Point()
atexit自动释放单例指针
~Person()
atexit自动释放单例指针
*/
实现方式2,嵌套类自动释放单例指针
注意嵌套类静态成员的初始化
#include "Singleton.h"
class Point
{
public:
Point(int ix = 0, int iy = 0)
: _ix(ix)
, _iy(iy)
{ cout << "Point(int=0,int=0)" << endl; }
void print() const
{
cout << "(" << _ix
<< "," << _iy
<< ")" << endl;
}
~Point()
{
cout << "~Point()" << endl;
}
private:
int _ix;
int _iy;
};
int main(void)
{
Point * pt1 = Singleton::getInstance(1, 2);
Point * pt2 = Singleton::getInstance(3, 4);
pt1->print();
pt2->print();
cout << "p1 = " << pt1 << endl
<< "p2 = " << pt2 << endl;
return 0;
}
Singleton.h
#include
using std::cout;
using std::endl;
template
class Singleton
{
public:
template //
static T * getInstance(Args... args)//T1 t1, T2 t2//T1 = int a float b double d
{
if(nullptr == _pInstance)
{
_pInstance = new T(args...);//args可变参数列表
_auto;//为了在模板参数推导时创建_auto对象
}
return _pInstance;
}
private:
class AutoRelease//嵌套类释放单例指针
{
public:
AutoRelease()
{
cout << "AutoRelease()" << endl;
}
~AutoRelease()
{
if(_pInstance)
{
delete _pInstance;
cout << "~AutoRelease()" << endl;
}
}
};
private:
Singleton()
{
cout << "Singleton()" << endl;
}
~Singleton()
{
cout << "~Singleton()" << endl;
}
private:
static T * _pInstance;
static AutoRelease _auto;//int a
};
template
T * Singleton::_pInstance = nullptr;
template
typename Singleton::AutoRelease Singleton::_auto;
//嵌套类成员初始化模板
6.友元函数类外实现
friend void myPrint<>(Person
&p);
//注意<>,类内实现不用加<>
#include
using namespace std;
template //要让友元看到声明
class Person;
template//一般写在前面
void myPrint(Person &p)
{
cout<
class Person
{
/*
friend void myPrint(Person &p)
{
cout<(Person &p);
//注意<>,类内实现不用加<>
public:
Person(T1 name, T2 age)
:_name(name),_age(age)
{}
private:
T1 _name;
T2 _age;
};
void test01()
{
Personp("沙和尚",20);
myPrint(p);
}
int main()
{
test01();
return 0;
}
/*
沙和尚
20
*/
1、模板的参数类型有哪些?各自有哪些特点?
(1)类型参数:
class/typename T 就是类型参数(2)非类型参数:
常量表达式,整形:bool/char/short/int/long/size_t ,注意:float/double这些不是整型无论是类型参数还是非类型参数都可以设定默认值
2、函数模板有几种实例化的方式?函数模板可以重载吗?函数模板的使用需要注意哪些问题?
(1)显示实例化
(2)隐式实例化--由编译器自动推导
(1)函数模版与普通函数是可以重载的(此时普通函数的优先级比函数模版高)
(2)函数模版和函数模版是可以重载的
(1)模版不能写成头文件和实现文件的形式,或者说不能将声明和实现分开。否则在链接时会出错
(2)如果只有函数模版,再给出不完全一致的参数的情况就会报错,typename T只能被推导为一个确定的类型。
(3)函数模版在使用时,应该考虑某些边缘情况,对模版进行特化。
3、可变模板参数有哪些特点?
//模版参数包
templateclass tuple; //函数参数包
template
void f(T ...args);
(1)函数参数包要求必须唯一,且是函数的最后一个参数;模版参数包则没有。
(2)...在参数右侧,是解包; ...在参数左侧,是打包。
(3)参数个数和参数类型在编译时,由模版推导确定。