模板泛型编程
1.简介
泛型,是在运行时确定类型,而不是在编译时确定类型。主要是将类型通用化,减少代码量,实现通用化。
2.函数模板
等价于java中泛型函数,
template<typename T> 通用模板,T主要是告诉编译器后面代码中T不要报错,在运行时确定类型。
#include <iostream>
using namespace std;
template <typename T>
void swap2(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
int main(int argc, char const *argv[])
{
int a = 10;
int b = 20;
swap2(a, b);
cout << a << b << endl;
system("pause");
return 0;
}
mySwap<int>(a, b);//可以直接确定类型
3.泛型模板注意事项
1.推导的类型,必须一致
2.模板必须要确定出T的具体类型,才可以使用
4.函数模板2
template 能用于函数模板中
template <class T>
void swap2(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
#include <iostream>
using namespace std;
template <typename T>
class Complex
{
public:
//构造函数
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
//运算符重载
Complex<T> operator+(Complex &c)
{
Complex<T> tmp(this->a + c.a, this->b + c.b);
return tmp;
}
private:
T a;
T b;
};
int main()
{
//对象的定义,必须声明模板类型,因为要分配内容
Complex<int> a(10, 20);
Complex<int> b(20, 30);
Complex<int> c = a + b;
system("pause");
return 0;
}
5.普通函数和泛型函数区别
1.普通函数调用可以发生隐式转换
2.函数模板 用自动类型推导,不可以发生隐式类型转换
3.函数模板用显示指定类型,可以发生隐式类型转换.
隐式转换:
#include <iostream>
#include <string>
using namespace std;
void swap3(int a, int c)
{
int temp = c;
c = a;
a = temp;
}
int main(int argc, char const *argv[])
{
int a = 20;
char c = 'c';
swap3(a, c);
system("pause");
return 0;
}
2.函数模板不可以发生隐式类型转换
#include <iostream>
#include <string>
using namespace std;
template <typename T>
void swap3(T a, T c)
{
T temp = c;
c = a;
a = temp;
}
int main(int argc, char const *argv[])
{
int a = 20;
char c = 'c';
swap3(a, c);//报错
system("pause");
return 0;
}
3.指定类型,发生隐式转换
swap3<int>(a, c);//不报错
6.普通函数与泛型函数调用规则
1.如果函数模板和普通函数都可以实现,普通优化
2.可以使用空模板参数列表来强制调用函数模板
3.函数模板也可以发生重载
4.如果函数模板剋产生更好的匹配,有限调用函数模板
#include <iostream>
#include <string>
using namespace std;
void swap4(int &a, int &b)
{
cout << "普通函数调用了" << endl;
int temp = a;
a = b;
b = temp;
}
template <typename T>
void swap4(T &a, T &b)
{
cout << "函数模板调用了" << endl;
T temp = a;
a = b;
b = temp;
}
int main(int argc, char const *argv[])
{
int a = 10;
int b = 20;
swap4(a, b);//普通函数调用了
swap4<>(a, b);//函数模板调用了,加空模板
system("pause");
return 0;
}
3模板重载
#include <iostream>
#include <string>
using namespace std;
void swap4(int &a, int &b)
{
cout << "普通函数调用了" << endl;
int temp = a;
a = b;
b = temp;
}
template <typename T>
void swap4(T &a, T &b)
{
cout << "函数模板调用了" << endl;
T temp = a;
a = b;
b = temp;
}
template <typename T>
void swap5(T &a, T &b, T &c)
{
cout << a << b << c;
}
int main(int argc, char const *argv[])
{
int a = 10;
int b = 20;
int c = 30;
swap4(a, b);
swap4<>(a, b);
swap5(a, b, c);
system("pause");
return 0;
}
4.如果函数模板剋产生更好的匹配
7.模板的通用性
模板有其限制性,比如比较时,比较基本类型,但是如果是比较类对象,则可能会有问题。
在争对非基本类型时,有其局限性,比如比较。
#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Base
{
public:
T1 name;
T2 age;
public:
Base(T1 name, T2 age);
};
template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
//比较
template <typename T1, typename T2>
bool compare(Base<T1, T2> &b1, Base<T1, T2> &b2)
{
if (b1.name == b2.name && b1.age == b2.age)
{
return true;
}
return false;
}
void test()
{
Base<string, int> base("zhangsan", 20);
Base<string, int> base2("zhangsan", 20);
bool flag = compare(base, base2);
cout << flag << endl;
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
8.模板显示化调用
#include <iostream>
using namespace std;
template <typename T>
void swap5(T &a, T &b);
class Job
{
public:
string name;
int age;
public:
Job(string name, int age);
};
Job::Job(string name, int age)
{
this->name = name;
this->age = age;
}
template <>
void swap5(Job &job1, Job &job2)
{
string tempName = job1.name;
int tempAge = job1.age;
job1.name = job2.name;
job1.age = job2.age;
job2.name = tempName;
job2.age = tempAge;
}
template <typename T>
void swap5(T &a, T &b) // general version
{
T temp;
temp = a;
a = b;
b = temp;
}
void test()
{
int a = 10;
int b = 20;
swap5(a, b);
Job job1("zhangsan", 20);
Job job2("lisi", 30);
swap5<>(job1, job2);//具显化调用
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
9.模板实例化调用
#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Base
{
public:
T1 name;
T2 age;
public:
Base(T1 name, T2 age);
T1 getName() const;
T2 getAge() const;
};
template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template <typename T1, typename T2>
T1 Base<T1, T2>::getName() const
{
return this->name;
}
template <typename T1, typename T2>
T2 Base<T1, T2>::getAge() const
{
return this->age;
}
void test()
{
Base<string, int> base("zhangsan", 20);
cout << base.getName() << base.getAge() << endl;
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
10.模板多文件编程
1.当不涉及泛型类模板时
类的声明在.h文件中,类的实现在.cpp文件中,主程序main.cpp文件,只需添加.h文件就可以调用,或者调用.cpp也可以
原因:调用.h文件时,不是泛型模板,则编译时,就会去找实现,全局声明。
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include "01fun.h"
int main(int argc, char const *argv[])
{
char *s1 = (char *)"hello";
char *s2 = (char *)"world";
char *s3 = strCatStr(s1, s2);
printf("%s\n", s3);
free(s3);
system("pause");
return 0;
}
01fun.h
#ifndef FUN_01
#define FUN_01
char *strCatStr(char *arr1, char *arr2);
#endif
01fun.c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <01fun.h>
char *strCatStr(char *arr1, char *arr2)
{
char *result = (char *)malloc(strlen(arr1) + strlen(arr2) + 1); //1作为结束位置'\0',不会默认添加'\0'
if (result == NULL)
{
exit(1);
}
char *temp = result; //记录首地址
while (*arr1 != '\0')
{
*result = *arr1;
arr1++;
result++;
}
while (*arr2 != '\0')
{
*result = *arr2;
arr2++;
result++;
}
*result = '\0';//作为结束标志
printf("%c\n", *result);
return temp;
}
2.当涉及泛型类模板时
两种形式:
1.类声明.h,类实现.cpp,主程序main.cpp,但是只能添加.cpp才能执行类模板
2.类声明实现.hpp,主程序main.cpp,添加.hpp文件执行类模板。
原因:泛型类模板和函数模板,编译器不会再编译器时就确定要执行哪个文件,所以声明的.h文件不会去实现.cpp中的函数模板,所以要不直接执行.cpp文件,要不声明.h和.cpp结合,否则会编译失败
main.cpp
#include <iostream>
#include "11func.cpp"
using namespace std;
void test()
{
BaseAdapter<string, int> BaseAdapter("zhangsan", 20);
cout << BaseAdapter.getName() << BaseAdapter.getAge() << endl;
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
11func.h
template <typename T1, typename T2>
class BaseAdapter
{
public:
T1 name;
T2 age;
public:
BaseAdapter(T1 name, T2 age);
T1 getName() const;
T2 getAge() const;
};
11func.cpp
#include "11func.h"
template <typename T1, typename T2>
BaseAdapter<T1, T2>::BaseAdapter(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template <typename T1, typename T2>
T1 BaseAdapter<T1, T2>::getName() const
{
return this->name;
}
template <typename T1, typename T2>
T2 BaseAdapter<T1, T2>::getAge() const
{
return this->age;
}
如果使用.hpp文件
template <typename T1, typename T2>
class BaseAdapter
{
public:
T1 name;
T2 age;
public:
BaseAdapter(T1 name, T2 age);
T1 getName() const;
T2 getAge() const;
};
template <typename T1, typename T2>
BaseAdapter<T1, T2>::BaseAdapter(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template <typename T1, typename T2>
T1 BaseAdapter<T1, T2>::getName() const
{
return this->name;
}
template <typename T1, typename T2>
T2 BaseAdapter<T1, T2>::getAge() const
{
return this->age;
}
11.hpp模板使用注意点
- 1.不可包含全局对象和全局函数。
- 2.类之间不可循环调用
- 3.不可使用静态成员
3唯一的列外,唯一的例外是const static整型成员,因为在vs2003中,该类型允许在定义时初始化。
class A{
public:
const static int intValue = 123;
};
1.类中仅有一个静态成员时,且仅有一个调用者时,可以通过局域静态变量模拟
//方法模拟获取静态成员
someType getMember()
{
static someTypevalue(xxx);//作用域内静态变量
return value;
}
2.类中有多个方法需要调用静态成员,而且可能存在多个静态成员时,可以将每个静态成员封装一个模拟方法,供其他方法调用。
someType getMemberA()
{
static someTypevalue(xxx);//作用域内静态变量
return value;
}
someType getMemberB()
{
static someTypevalue(xxx);//作用域内静态变量
return value;
}
void accessMemberA()
{
someType member = getMemberA();//获取静态成员
};
//获取两个静态成员
void accessStaticMember()
{
someType a = getMemberA();//获取静态成员
someType b = getMemberB();
};
3.第二种方法对于大部分情况是通用的,但是当所需的静态成员过多时,编写封装方法的工作量将非常巨大,在此种情况下,建议使用Singleton模式,将被调用类定义成普通类,然后使用Singleton将其变为全局唯一的对象进行调用。
如原h+cpp下的定义如下:
class A{
public :
type getMember(){
return member;
}
static type member;//静态成员
}
12345678
采用singleton方式,实现代码可能如下(singleton实现请自行查阅相关文档)
//实际实现类
class Aprovider{
public :
type getMember(){
return member;
}
type member;//变为普通成员
}
//提供给调用者的接口类
class A{
public :
type getMember(){
return Singleton<AProvider >::getInstance()->getMember();
}
}
12.泛型类模板继承
1.子类继承父类
与其他语言差不多,java中也是一样,需要实现泛型函数类型,编译器需要确定
#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Base
{
public:
T1 name;
T2 age;
public:
Base(){};
Base(T1 name, T2 age);
T1 getName() const; //这只是虚函数,并不是纯虚函数,Base不是抽象类,用于解决多态问题
T2 getAge() const;
virtual void show() const;
};
template <typename T1, typename T2>
void Base<T1, T2>::show() const
{
cout << this->name << this->age << endl;
}
template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template <typename T1, typename T2>
T1 Base<T1, T2>::getName() const
{
return this->name;
}
template <typename T1, typename T2>
T2 Base<T1, T2>::getAge() const
{
return this->age;
}
class Son : public Base<string, int>
{
public:
int salary;
public:
Son(string name, int age, int salary);
string getName() const;
virtual void show() const;
};
Son::Son(string name, int age, int salary) : Base(name, age)
{
this->salary = salary;
}
string Son::getName() const
{
return this->name;
}
void Son::show() const
{
cout << this->name << this->age << "薪资:"<<this->salary << endl;
}
void test()
{
Base<string,int> *son = new Son("zhangsan", 20, 10000);
son->show();
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
2.子类也是泛型模板
子类是泛型模板,则可以等到实现的时候去确定类型,泛型,编译器会过滤,等到执行的时候才会运行。
#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Base
{
public:
T1 name;
T2 age;
public:
Base(){};
Base(T1 name, T2 age);
T1 getName() const; //这只是虚函数,并不是纯虚函数,Base不是抽象类,用于解决多态问题
T2 getAge() const;
virtual void show() const;
};
template <typename T1, typename T2>
void Base<T1, T2>::show() const
{
cout << this->name << this->age << endl;
}
template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template <typename T1, typename T2>
T1 Base<T1, T2>::getName() const
{
return this->name;
}
template <typename T1, typename T2>
T2 Base<T1, T2>::getAge() const
{
return this->age;
}
class Son : public Base<string, int>
{
public:
int salary;
public:
Son(string name, int age, int salary);
string getName() const;
virtual void show() const;
};
Son::Son(string name, int age, int salary) : Base(name, age)
{
this->salary = salary;
}
string Son::getName() const
{
return this->name;
}
void Son::show() const
{
cout << this->name << this->age << "薪资:"<<this->salary << endl;
}
void test()
{
Base<string,int> *son = new Son("zhangsan", 20, 10000);
son->show();
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
3.继承 A->B->C
#include <iostream>
using namespace std;
template <typename T>
class IBase //当作接口使用,全部都是纯虚抽象函数,则基本等价于java中的接口概念
{
public:
virtual void setSalary(T salary) = 0;
virtual T getSalary() const = 0;
};
template <typename T>
class Base : public IBase<T>
{
public:
int age;
T baseSalary;
public:
virtual void setAge(int age) = 0;
};
class Student : public Base<string>
{
public:
virtual void setSalary(string salary);
virtual string getSalary() const;
virtual void setAge(int age);
};
void Student::setSalary(string salary)
{
this->baseSalary = salary;
}
string Student::getSalary() const
{
return this->baseSalary;
}
void Student::setAge(int age)
{
this->age = age;
}
void test()
{
Student stu;
stu.setAge(20);
stu.setSalary("10000");
cout << stu.getSalary() << "年龄:" <<stu.age << endl;
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
13.类模板与友元
1.友元函数声明非模板函数
非模板函数,如果只是使用一个模板作为参数,这意味着必须要为使用的友元定义显式具体化
#include <iostream>
using namespace std;
template <class T>
class Base
{
private:
string name;
T age;
public:
Base(/* args */);
Base(string name, T age);
~Base();
friend void show(Base<T> &b);//show非模板函数,只是使用一个模板作为参数,这意味着必须要为使用的友元定义显式具体化
// {
// cout << b.name << b.age << endl; //如果需要访问Base中的name和age,需要编程Base的友元
// }
};
template <class T>
Base<T>::Base(/* args */)
{
}
template <class T>
Base<T>::Base(string name, T age)
{
this->name = name;
this->age = age;
}
template <class T>
Base<T>::~Base()
{
}
void show(Base<int> &b)//具显化使用
{
cout << b.name << b.age << endl; //如果需要访问Base中的name和age,需要编程Base的友元
}
void test()
{
Base<int> b("zhangsan", 20);
show(b);
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
2.友元函数模板
当前只能使用模板类的非约束模板友元函数。
#include <iostream>
using namespace std;
template <class T>
class HasFriend
{
private:
string name;
T age;
public:
HasFriend(string name, T age);
template <class S>
friend void count();
template <class S>
friend void report(HasFriend<S> &h);//友元函数不要使用与HasFriend相同class T,会有警告,甚至可能造成编译错误
};
template <class T>
HasFriend<T>::HasFriend(string name, T age)
{
this->name = name;
this->age = age;
}
template <class S>
void count()
{
cout << sizeof(HasFriend<S>) << endl;
}
template <class T>
void report(HasFriend<T> &h)
{
cout << h.name << h.age << endl;
}
void test()
{
HasFriend<int> has("zhangsan", 30);
count<int>();
report(has);
}
int main(int argc, char const *argv[])
{
test();
system("pause");
return 0;
}
注意:<>不能再使用了,在以前,它有两层意思,一是,表明此友元函数是函数模板; 二是,此模板使用的模板类型参数为当前 模板类的类型参数class,但是不能使用了,请使用上面那种
下面这种是之前的写法:模板的约束模板友元函数 不能在使用了。
#include <iostream>
using namespace std;
template <class T>
class Test
{
private:
T num;
public:
Test(T n)
{
num = n;
}
friend ostream &operator<<<>(ostream &out, const Test<T> &);
};
template <class T>
ostream &operator<<<>(ostream &out, const Test<T> &obj)
{
out << obj.num;
return out;
};
void main()
{
Test<int> t(414);
cout << t;
}
14.类模板的静态成员
类中的static大家应该都清楚,只会初始化一次,后续都是赋值,不改变内存空间地址。
但是类模板中,编译器采用了二级编译机制,编译器遇到类模板,不会编译泛型,只在执行时确定了泛型类型,在编译。
这相当于在程序中每遇到一种具体的类就编译一次,
注意:最后结果就是每个类模板实例化的类都有属于他们自己的static成员。这句话测试是错误的,不是每个类模板都有属于自己的static成员,而是
相同类型的,有共有的static成员,非相同类型的,有各自的static成员。
#include <iostream>
using namespace std;
template <typename T>
class Obj
{
public:
static T m_t;
};
template <typename T>
T Obj<T>::m_t = 0;
int main()
{
Obj<int> i1, i2, i3;
i1.m_t = 10;
i2.m_t++;
i3.m_t++;
cout << Obj<int>::m_t << endl;
cout << i1.m_t << endl;//12
Obj<float> f1, f2, f3;//float类型,static m_t有新的成员
f1.m_t = 10;
f2.m_t++;
f3.m_t++;
cout << Obj<float>::m_t << endl;//12.0
Obj<char> c1, c2, c3;c
c1.m_t = 'a';
c2.m_t++;
c3.m_t++;
cout << Obj<char>::m_t << endl;//c
cout << i1.m_t << endl; //12
cout << Obj<int>::m_t << endl;//12,类也是如此
system("pause");
return 1;
}
12
12
12
c
12
请按任意键继续. . .
建议:不推荐使用类模板的静态成员,很容易出问题。
};
void main()
{
Test t(414);
cout << t;
}
### 14.类模板的静态成员
**类中的static大家应该都清楚**,只会初始化一次,后续都是赋值,不改变内存空间地址。
但是**类模板**中,编译器采用了二级编译机制,编译器遇到类模板,不会编译泛型,只在执行时确定了泛型类型,在编译。
**这相当于在程序中每遇到一种具体的类就编译一次**,
注意:**最后结果就是每个类模板实例化的类都有属于他们自己的static成员。这句话测试是错误的,不是每个类模板都有属于自己的static成员,而是**
**相同类型的,有共有的static成员,非相同类型的,有各自的static成员。**
```c++
#include <iostream>
using namespace std;
template <typename T>
class Obj
{
public:
static T m_t;
};
template <typename T>
T Obj<T>::m_t = 0;
int main()
{
Obj<int> i1, i2, i3;
i1.m_t = 10;
i2.m_t++;
i3.m_t++;
cout << Obj<int>::m_t << endl;
cout << i1.m_t << endl;//12
Obj<float> f1, f2, f3;//float类型,static m_t有新的成员
f1.m_t = 10;
f2.m_t++;
f3.m_t++;
cout << Obj<float>::m_t << endl;//12.0
Obj<char> c1, c2, c3;c
c1.m_t = 'a';
c2.m_t++;
c3.m_t++;
cout << Obj<char>::m_t << endl;//c
cout << i1.m_t << endl; //12
cout << Obj<int>::m_t << endl;//12,类也是如此
system("pause");
return 1;
}
12
12
12
c
12
请按任意键继续. . .
建议:不推荐使用类模板的静态成员,很容易出问题。