C++学习笔记:模板
模板分为函数模板与类模板。
1.函数模板
函数模板可以建立一个通用的函数,其返回值和参数的类型无需提前确定,用一个虚拟的类型代表。
语法:
template<typename T>
//函数声明或定义
//template:表明创建一个模板
//typename:表明后面是一种数据类型
//T:通用数据类型
例程:
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void myswap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 5;
myswap(a, b);
cout << "a:" << a << endl;
cout << "b:" << b << endl;
}
int main() {
test01();
return 0;
}
函数模板使用有两个注意点:
1.模板进行自动类型推导必须要推导出一致的数据类型T
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void myswap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 5;
char c = 'c';
myswap(a, b);//正确
myswap(a, c);//T类型不一致,错误
cout << "a:" << a << endl;
cout << "b:" << b << endl;
}
int main() {
test01();
return 0;
}
2.模板必须要确定数据类型T才可使用
template<typename T>
void fun()
{
cout<<"fun调用"<<endl;
}
fun<int>();//此时调用fun()需要进行显示指定类型
2.类模板
类模板可以建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型表示。
语法:
template<class T>
//后面写一个类
例程:
#include <iostream>
#include <string>
using namespace std;
template<typename nametype, typename agetype>
class Person
{
public:
nametype m_name;
agetype m_age;
Person(nametype name,agetype age)
{
this->m_age = age;
this->m_name = name;
}
void show()
{
cout << this->m_name << ":" << this->m_age << endl;
}
};
void test01()
{
Person <string, int>p1("Mike", 20);
p1.show();
}
int main() {
test01();
return 0;
}
2.1类模板注意事项
1.类模板没有自动类型推导的使用方式;
2.类模板在模板参数列表中可以有默认参数。
#include <iostream>
#include <string>
using namespace std;
template<typename nametype, typename agetype = int>
class Person
{
public:
nametype m_name;
agetype m_age;
Person(nametype name,agetype age)
{
this->m_age = age;
this->m_name = name;
}
void show()
{
cout << this->m_name << ":" << this->m_age << endl;
}
};
void test01()
{
//Person p1("Mike", 20);无法用自动类型推导
Person<string,int> p1("Mike", 20);//显示类型推导
p1.show();
}
void test02()
{
Person<string> p1("Mike", 21);//agetype的类型为默认的int
p1.show();
}
int main() {
test01();
test02();
return 0;
}
2.2类模板中函数的创建时机
普通类:一开始就创建;
类模板:成员函数在调用时才创建。
#include <iostream>
#include <string>
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout << "Person1调用" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "Person2调用" << endl;
}
};
//创建类模板
template<class T>
class myclass
{
public:
T obj;
void fun1()
{
obj.showPerson1();
}
void fun2()
{
obj.showPerson2();
}
};
void test01()
{
myclass<Person1>p;
p.fun1();
//p.fun2();
}
void test02()
{
myclass<Person2>p;
//p.fun1();
p.fun2();
}
int main()
{
test01();
test02();
return 0;
}
2.3类模板对象作函数参数时
有三种方式将对象传入函数
1.指定传入类型,即显示推导数据的类型
2.参数模板化,传入具体的对象时确定了参数类型的对象时模板自动推导数据的类型;
3.整个类模板化传入
#include <iostream>
#include <string>
using namespace std;
//创建类模板
template<class T1,class T2>
class Person
{
public:
T1 name;
T2 age;
Person(T1 m_name,T2 m_age)
{
this->age = m_age;
this->name = m_name;
}
void showPerson()
{
cout << this->name << ":" << this->age << endl;
}
};
//1.指定传入类型
void printPerson01(Person<string, int> &p)
{//在传入时确定模板的参数
p.showPerson();
}
void test01()
{
Person<string, int> p("Mike", 20);
printPerson01(p);
}
//2.参数模板化
template<class T1, class T2>
void printPerson02(Person<T1, T2>& p)
{
p.showPerson();
cout << "T1类型为:" << typeid(T1).name() << endl;
cout << "T2类型为:" << typeid(T2).name() << endl;
}
void test02()
{
Person<string, int> p("Mike", 20);
printPerson02(p);
}
//3.整个类模板化
template<class T>
void printPerson03(T& p)
{
p.showPerson();
cout << "T的数据类型为:" << typeid(T).name() << endl;
}
void test03()
{
Person<string, int> p("Mike", 20);
printPerson03(p);
}
int main()
{
//test01();
//test02();
test03();
return 0;
}
2.4类模板与继承
当父类是一个模板
1.子类直接继承时需要显示推导父类模板中的T的类型;
例程:
template<class T>
class base
{
T m;
};
//class son :public base//错误,需要确定父类中T的类型
class Son1 :public base<int>
{
};
2.如果需要灵活指定父类中的T数据类型,子类也需要变为模板;
例程:
template<class T>
class base
{
T m;
};
//如果需要灵活指定父类的T类型,子类也需要变为模板
template<class T1, class T2>
class Son2 :public base<T2>
{
T1 obj;
};
2.5类模板分文件编写
类模板函数在类外实现时要加前缀
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
在分文件编写时,将含模板的类声明和实现写到一起,文件后缀名改为.hpp,例程如下。
//person.hpp文件
#pragma once
#include <iostream>
using namespace std;
#include <string>
template<class T1, class T2>
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
主函数如下:
#include<iostream>
using namespace std;
//将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"
void test01()
{
Person<string, int> p("Tom", 10);
p.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}
2.6类模板友元
2.6.1类内实现
直接在类内声明友元即可,例程如下:
//全局函数,类内实现
//类内声明
friend void showPerson(Person<T1, T2>& p)
{
cout << p.name << ":" << p.age << endl;
}
void test01()
{
Person<string, int>p("Tom", 20);
showPerson(p);
}
2.6.2类外实现
需要提前让编译器得知全局函数的存在
在类前加入如下代码:
//声明类与函数
//声明类,同时该类是一个模板类
template<class T1, class T2>
class Person;
//声明函数,同时该函数是一个模板
template<class T1, class T2>
void showPerson1(Person<T1, T2>& p);
在类内声明时也要加空列表
//类外实现
friend void showPerson1<>(Person<T1, T2>& p);
类外调用
template<class T1, class T2>
void showPerson1(Person<T1, T2>& p)
{
cout << p.name << ":" << p.age << endl;
}
void test02()
{
Person<string, int>p("Tom", 21);
showPerson1(p);
}
完整例程如下:
#include <iostream>
#include <string>
using namespace std;
//声明类与函数
//声明类,同时该类是一个模板类
template<class T1, class T2>
class Person;
//声明函数,同时该函数是一个模板
template<class T1, class T2>
void showPerson1(Person<T1, T2>& p);
//创建类模板
template<class T1, class T2>
class Person
{
public:
T1 name;
T2 age;
Person(T1 m_name, T2 m_age)
{
this->age = m_age;
this->name = m_name;
}
//全局函数,类内实现
friend void showPerson( Person<T1, T2>& p)
{
cout << p.name << ":" << p.age << endl;
}
//类外实现
friend void showPerson1<>(Person<T1, T2>& p);
};
void test01()
{
Person<string, int>p("Tom", 20);
showPerson(p);
}
//类外实现,需要加空模板参数,同时提前申明函数和类供编译器查询
template<class T1, class T2>
void showPerson1(Person<T1, T2>& p)
{
cout << p.name << ":" << p.age << endl;
}
void test02()
{
Person<string, int>p("Tom", 21);
showPerson1(p);
}
int main()
{
//test01();
test02();
return 0;
}
3.例程
创建一个数组模板,实现如下功能
1.实现含参构造;
2.实现拷贝构造,深拷贝;
3.析构函数清理数据;
4.重载 = 避免浅拷贝;
5.可供下标查看数据;
6.可以进行append,pop等数组尾部增删操作。
mylist.hpp文件编写如下:
#pragma once
#include <iostream>
using namespace std;
#include <string>
template<class T>
class myarray
{
public:
myarray(int capacity)
{//构造函数
this->m_capacity = capacity;
this->m_size = 0;
this->pAddress = new T[this->m_capacity];
}
myarray(const myarray& arr)
{//拷贝构造函数
this->m_capacity = arr.m_capacity;
this->m_size = arr.m_size;
this->pAddress = new T[this->m_capacity];//深拷贝
//数据拷贝
for (int i = 0; i < this->m_size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
~myarray()
{//析构函数
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
}
}
//operator= 防止浅拷贝
myarray& operator=(const myarray& arr)
{
//先判断原来的堆区是否有数据并释放
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
}
this->m_capacity = arr.m_capacity;
this->m_size = arr.m_size;
this->pAddress = new T[this->m_capacity];//深拷贝
for (int i = 0; i < arr.m_size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
void append(const T str)
{
if (this->m_size >= this->m_capacity)
{
cout << "数组已满" << endl;
return;
}
else
{
this->pAddress[this->m_size] = str;
this->m_size++;
}
}
void pop()
{
if (this->m_size = 0)
{
cout << "数组已空" << endl;
return;
}
else
{
this->m_size--;
}
}
T& operator[](int index)
{
return this->pAddress[index];
}
int getcapacity()
{
return this->m_capacity;
}
int getsize()
{
return this->m_size;
}
private:
T* pAddress;//指针指向堆区开辟的数组
int m_capacity;//数组容量大小
int m_size;//数组内元素个数
};
cpp文件编写如下:
#include <iostream>
#include <string>
using namespace std;
#include"mylist.hpp"
void printarray(myarray<int>& p)
{
for (int i = 0; i < p.getsize(); i++)
{
cout << p[i] << endl;
}
}
void test01()
{
myarray<int>arr1(5);
arr1.append(1);
arr1.append(2);
arr1.append(3);
arr1.append(4);
arr1.append(5);
printarray(arr1);
}
int main()
{
test01();
return 0;
}