函数模板
-
函数中用到的未知类型
-
函数模板的调用
- 隐式调用:根据数据类型自动推导模板类型
- 显式调用:显示传入类型,格式
Func<Type>(arg)
-
模板可以缺省(不传参则自动赋初值)
-
模板可以写变量
-
一个类的成员函数也可以是模板
#include
#include #include using namespace std; // 另一个类的成员函数是函数模板 class MM { public: //函数模板 template void print(T data) { cout << data << endl; } protected: }; //函数模板重载 //函数模板和普通函数 template void printSum(T1 a, T2 b, T3 c) { cout << "调用函数模板" << endl; cout << a << endl; cout << b << endl; cout << c << endl; } void printSum(int a, int b, int c) { cout << "调用普通函数" << endl; cout << a << endl; cout << b << endl; cout << c << endl; } int main() { MM test; test.print (3); test.print(3.33); test.print ("asvsa"); printSum(1, 2, 3); //隐式调用 printSum (1, 2, 3); //显示调用 printSum (33, 2.333, "abcd"); return 0; }
类模板
- 类用到了位置类型
- 多文件中,类模板不能把声明和实现分开
- 类模板在调用时必须显示调用,不能隐式调用
- 类模板不是一个完整类,所以在用到类名的地方,必须用类名<类型>方式去使用
- 类模板的特化
#include <iostream>
#include <string>
#include <array>
using namespace std;
template <class T1, class T2>
class Test
{
public:
Test() {}
Test(T1 a, T2 b) : a(a), b(b) {}
void print();
protected:
T1 a;
T2 b;
};
void run_test()
{
Test<string, string> test("abc", "abcd"); //类模板必须显示实例化
Test<int, string> *p = new Test<int, string>(6, "hhan");
test.print();
p->print();
}
// 用到类型的地方必须用类名<类型>的方式使用
template <class T1, class T2>
void Test<T1, T2>::print()
{
cout << a << endl;
cout << b << endl;
}
template <class T1, class T2>
class Test2 : public Test<T1, T2>
{
public:
Test2() {}
protected:
};
template <class T, int size>
class MyArray
{
public:
MyArray()
{
memory = new T[size];
}
protected:
T* memory;
};
void testMyArray()
{
MyArray<int, 3> arr;
array<Test<int, string>, 5> data;
//上面一句话等效于下面两句话
using dataT = Test<int, string>;
array<dataT, 5> data1;
};
//函数特化
template <class T1, class T2, class T3>
class Data
{
public:
Data(T1 a, T2 b, T3 c) : a(a), b(b), c(c)
{
cout << "调用原生模板" << endl;
}
protected:
T1 a;
T2 b;
T3 c;
};
//局部特化,特殊化处理
template <class T>
class Data<T, T, T>
{
public:
Data(T a, T b, T c) : a(a), b(b), c(c)
{
cout << "调用局部特化" <<endl;
}
protected:
T a, b, c;
};
//完全特化 --> 针对特定数据进行特定处理
template<>
class Data<int, string, string>
{
public:
Data(int a, string b, string c) : a(a), b(b), c(c)
{
cout << "调用完全特化" << endl;
}
protected:
int a;
string b;
string c;
};
void testCallData()
{
Data<int, string, string> dataTest(2, "hhan", "ann");
Data<string, string, string> dataTest2("huysj", "hhan", "ann");
Data <int, double, string> dataTest3(1, 3.14, "hhan");
}
int main()
{
run_test();
testCallData();
return 0;
}
可变模板
-
知道折叠参数的类型:
_Ty… args
- 类型:
...Args
- 参数包(变量名):
args
- 类型:
-
知道折叠参数的展开方式
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
//特化函数
template <class T>
void printData(T data)
{
cout << data << " ";
}
template <class ... Args>
void print(Args... args)
{
int array[] = {(printData(args), 0)...};
cout << endl;
}
int main()
{
print(1);
print(1, "hhan");
print(1, "hhan", "ann");
return 0;
}
初始化成员
template <class T1, class T2>
class Test
{
public:
Test(T1 a, T2 b) :a(a), b(b) {}
void print();
protected:
T1 a;
T2 b;
};
其中Test(T1 a, T2 b) :a(a), b(b) {}
用于初始化成员列表
成员初始化列表使用冒号(:
)后跟初始化表达式,每个初始化表达式由成员变量名和对应的初始化值组成,中间用逗号分隔。
通过使用成员初始化列表,可以直接对成员变量进行初始化,而不需要在构造函数的函数体中使用赋值操作。这样可以提高代码的效率和可读性,并且在某些情况下,成员初始化列表也可以用于初始化常量成员或引用成员。
-
初始化常量
class Test { private: const int constantMember; public: Test(int value) : constantMember(value) {} };
常量成员变量只能通过成员初始化列表来初始化,而不能在构造函数的函数体中进行赋值操作
-
初始化引用成员
引用成员变量只能在成员初始化列表中进行初始化,而不能在构造函数的函数体中重新绑定到其他对象
class Test { private: int& refMember; public: Test(int& value) : refMember(value) {} };
初始化成员的其他写法
-
在构造函数的函数体中使用赋值操作符(
=
)进行初始化Test(T1 a, T2 b) { this->a = a; this->b = b; }
-
在构造函数的函数体中使用成员初始化列表进行初始化(混合写法)
Test(T1 a, T2 b) : a(a) { this->b = b; }