C++学习之第十五天-模板与泛型编程

知识点总结

1.函数模板-泛型编程,类型参数化:template<typename/class T>,typename和class效果一样

    1.T是一个通用的数据类型,告诉编译器如果下面紧跟着的函数或者类中出现T,不要报错

2.模板的使用

            1.自动推导类型,编译器必须推导出一致的T数据类型,才能正常使用函数模板。

            2.显式指定模板类型:mySwap<int>(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 <string,int>&p)
            2.参数模板化:template <class T1,class T2>
                        void doWork2(Person <T1,T2>&p)
            3.整个类模板化:
                template <class T>
                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<<endl;
}
//在display中不能进行打印,把参数进行拆分来进行打印
template <typename T,typename... Args>//相当于class T1, class T2,......
void print(T t, Args... args)//省略号...位于参数左边的时候,为打包,相当于T1 t1, T2 t2....
{
    cout<<t<<" ";
    print(args...);//省略号...位于参数右边,为拆包
    //拆包递归进行打印
}
void test02()
{
    //print();
    print(1);
    print(1,2.2,true); 
    print(1,2.2,true,"hello world");
}

运行结果

1
1,2.2,1
1,2.2,1,hello world

test02()

#include <iostream>
using namespace std;
int sum()//终止递归的条件
{
    return 0;
}
template <class T, class... Args>//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)="<<sum(1,2,3,4,5,6,7,8,9,10)<<endl;
}
int main()
{
    test02();
    return 0;
}

/*运行结果:
sum(1,2,3,4,5,6,7,8,9,10)=55
*/

2.类模板实现通用类型Stack。

#include <iostream>
using namespace std;
#include <string>
class Person;
template <typename T=int, size_t k_Size=10>//默认容量设置为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 <typename T,size_t k_Size>
Stack<T,k_Size>::~Stack()
{
    if(_data)
    {
        delete [] _data;
        _data=nullptr;
    }
}

template <typename T,size_t k_Size>
bool Stack<T,k_Size>::full()const
{
    return _top==k_Size-1;
}
template <typename T,size_t k_Size>
bool Stack<T,k_Size>::empty()const
{
    return _top==-1;
}
template <typename T,size_t k_Size>
void Stack<T,k_Size>::push(const T &val)
{
    if(!full())
    {
        _data[++_top] = val;
    }
    else
    {
        cout<<"The stack is full"<<endl;
        return;
    }
}
template <typename T,size_t k_Size>
void Stack<T,k_Size>::pop()
{
    if(!empty())
    {
        --_top;
    }
    else
    {
        cout<<"The stack is empty()"<<endl;
        return;
    }
}
template <typename T,size_t k_Size>
T Stack<T,k_Size>::top()
{
    return _data[_top];
}
void test01()
{
    Stack<int,20> st;
    st.push(12);
    cout<<st.top()<<endl;
}
class Person
{
public:
    Person(){};//无参构造一定要有,在stack中去申请堆空间的时候会去调用无参构造函数
    Person(string name, int age)
    {
        _name = name;
        _age = age;
    }
    string _name;
    int _age;
};

void test02()
{
    Stack<Person> st;
    Person p("孙哲",14);
    st.push(p);
    cout<<st.top()._name<<" "
        <<st.top()._age<<endl;
}
int main()
{
    //test01();
    test02();
    return 0;
}

3.函数模板实现队列

#include <iostream>
#include <string>
using namespace std;

template <typename T, size_t capacity = 10>
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 <typename T,size_t capacity>
Queue<T,capacity>::~Queue()//析构函数
{
    if(_data)
    {
        delete[]_data;
        _data=nullptr;
    }
}
template <typename T, size_t capacity>
bool Queue<T,capacity>::full()const//判队满
{
    return (_rear+1)%capacity == _front;//循环队列
}

template <typename T, size_t capacity>
bool Queue<T,capacity>::empty()const
{
    return _rear == _front;//队空
}
template <typename T, size_t capacity>
void  Queue<T,capacity>::enQueue(const T &val)
{
    if(!full())
    {
        _data[_rear] = val;//先入队,再加1
        _rear = (_rear+1)%capacity;
        ++_size;
    }
    else
    {
        cout<<"Queue is full"<<endl;
        return ;
    }
}

template <typename T, size_t capacity>
void  Queue<T,capacity>::deQueue()
{
    if(!empty())
    {
        _front = (_front+1)%capacity;//出队
        --_size;
    }
    else
    {
        cout<<"Queue is empty"<<endl;
        return ;
    }
}
template <typename T, size_t capacity>
T Queue<T,capacity>::get_front()const//获取队头元素
{
    if(!empty())
    {
        return _data[_front];
    }
}
template <typename T, size_t capacity>
T Queue<T,capacity>::get_rear()const//获取队尾元素
{
    if(!empty())
    {
        return _data[_rear-1];
    }
}
//测试1:int类型数据测试
void test01()
{
    Queue<int> 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="<<myQueue.get_front()<<endl;
        cout<<"size="<<myQueue.get_size()<<endl;
        myQueue.deQueue();
    }
    cout<<"size="<<myQueue.get_size()<<endl;
    
}
class Person//自定义类
{
public:
    Person(){};
    Person(string name,int age)
    :_name(name),_age(age)
    {

    }
    string _name;
    int _age;
};
//测试:自定义数据类型进行测试
void test02()
{
    Queue<Person> 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="<<qPerson.get_size()<<endl;
    while(!qPerson.empty())
    {
        cout<<"姓名:"<<qPerson.get_front()._name<<" "
            <<"年龄:"<<qPerson.get_front()._age<<endl;

        qPerson.deQueue();
    }
    cout<<"Queue size="<<qPerson.get_size()<<endl;
}
int main()
{
    //test01();
    test02();
    return 0;
}
/*
test02()运行结果:
Queue size=6
姓名:孙悟空 年龄:500
姓名:猪八戒 年龄:400
姓名:沙悟净 年龄:666
姓名:唐 僧 年龄:777
姓名:白龙马 年龄:888
姓名:土地老儿 年龄:999
Queue size=0
*/

4.实现一个模板形式的单例类,要求对于任意类型的类经过Singleton的处理之后,都能获取一个单例对象,并且可以传递任意参数

 1.可变模板类型包,可变函数参数包巩固练习

  2.单例模式复习

#include <iostream>
using namespace std;
#include<stdlib.h>
template <class T>
class Singleton
{
public:
    template <class ...Args>//可变模板类型包
    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自动释放单例指针"<<endl;
        }
    }
	Singleton()
    {
        cout << "Singleton()" << endl;
    }

	~Singleton()
    {
        cout << "~Singleton()" << endl;
    }
};
//1.函数模板初始化单例指针
template <class T>
T* Singleton<T>::_pInstance = nullptr;//饱汉模式

//Person自定义类
class Person
{
public:
    Person(string name,string age)
    :_name(name),_age(age)
    {
        cout<<"Person()"<<endl;
    }

    void showPerson()
    {
        cout<<_name<<"\t";
        cout<<_age<<endl;
    }
    ~Person()
    {
        cout<<"~Person()"<<endl;
    }
private:
    string _name;
    string _age;
};
//Point自定义类
class Point
{
public:
    Point(int ix,int iy)
    :_ix(ix),_iy(iy)
    {
        cout<<"Point(int,int)"<<endl;
    }
    void print()const
    {
        cout<<"("<<_ix<<","<<_iy<<")"<<endl;
    }
    ~Point()
    {
        cout<<"~Point()"<<endl;
    }
private:
    int _ix;
    int _iy;
};
//测试
void test01()
{
    Person *ps1 = Singleton<Person>::getInstance("苏武","28");
    ps1->showPerson();

    Point *pos1 = Singleton<Point>::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<Point>::getInstance(1, 2);
	Point * pt2 = Singleton<Point>::getInstance(3, 4);
	pt1->print();
	pt2->print();

	cout << "p1 = " << pt1 << endl
		 << "p2 = " << pt2 << endl;

	return 0;
}

Singleton.h

#include <iostream>
using std::cout;
using std::endl;


template <class T>
class Singleton
{
public:
	template <class... Args>//<typename T1, typename T2,......>
	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 <class T> 
T * Singleton<T>::_pInstance = nullptr;

template <class T>
typename Singleton<T>::AutoRelease Singleton<T>::_auto;
//嵌套类成员初始化模板

6.友元函数类外实现

 friend void myPrint<>(Person<T1,T2> &p);
    //注意<>,类内实现不用加<>

#include <iostream>
using namespace std;
template <class T1,class T2>//要让友元看到声明
class Person;

template<class T1,class T2>//一般写在前面
void myPrint(Person<T1,T2> &p)
{
    cout<<p._name<<endl
        <<p._age<<endl;
}

template <class T1,class T2>
class Person
{
	/*
	friend void myPrint(Person<T1,T2> &p)
	{
    	cout<<p._name<<endl
        <<p._age<<endl;
	}
	*/类内实现
    friend void myPrint<>(Person<T1,T2> &p);
    //注意<>,类内实现不用加<>
    
public:
    Person(T1 name, T2 age)
    :_name(name),_age(age)
    {}
private:
    T1 _name;
    T2 _age;
};
void test01()
{
    Person<string,int>p("沙和尚",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、可变模板参数有哪些特点?

//模版参数包
template<typename... Args> class tuple;

//函数参数包
template<typename... Args>
void f(T ...args);

(1)函数参数包要求必须唯一,且是函数的最后一个参数;模版参数包则没有。
(2)...在参数右侧,是解包; ...在参数左侧,是打包。
(3)参数个数和参数类型在编译时,由模版推导确定。

猜你喜欢

转载自blog.csdn.net/weixin_49278191/article/details/121342918
今日推荐