一、泛型编程\n概念:\n编写与类型无关的通用代码,达成代码复用,模板是泛型编程的基础。\n\n模板就是把工作交给编译器去做。让编译器去生成多个函数,省的我们再去写函数模板。比如Add加法函数。\n\n平时经常用的是函数模板和类模板\n\n二、函数模板\n函数模板格式:\n\ntemplate\u003Ctypename T1,typename T2,typename T3…>\n1\n这里需要有个感性的认知:\n\n1.一个模板参数只能定义一个函数。模板参数可以有缺省参数。\n\n2.模板参数是类型。函数参数是对象。\n模板参数传递的是类型,函数参数传递的是对象值。\n\n3.普通函数是有地址的,而模板函数没有地址。\n但是模板会推算,会通过实参传递给形参,推算他的实际类型。\n\ntemplate\u003Ctypename T>\nvoid Swap(T& left, T& right)\n{\n\tT temp = left;\n\tleft = right;\n\tright = temp;\n}\n1\n2\n3\n4\n5\n6\n7\n1.函数模板的实例化\n实例化:用不同类型的参数使用函数模板时,称为函数模板的实例化\n但是有时候也有例外。需要显式实例化。\n代码如下\n\ntemplate\u003Cclass T>\nT* func(int n)\n{\n\treturn new T[n];\n}\nint main()\n{\n\tint* p = func\u003Cint>(10);\n\treturn 0;\n}\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\ntypename是用来定义模板参数的关键字。也可以用class.但是不能用struct。\n\n三、类模板\n1.类模板和函数模板不同。函数模板一般可以显式传参推出实际类型。\n\n2.而类模板不能传参推断类型,所以类模板需要在类名的 后面加尖括号\u003C>里面加类型,这叫做类模板的显式实例化\n\n例如:\n\nvector\u003Cint> v1;//int类型\nvector\u003Cdouble> v2;//元素是double类型\n1\n2\n1.类模板的定义格式\n注意:\ncao不是具体的一个类,而是编译器根据被实例化的类型生成具体类的模具。\n\ntemplate\u003Cclass T1, class T2>\nclass 类模板名\n{\n\t//类内成员定义\n};\n//类模板\ntemplate\u003Cclass T>\nclass cao\n{\n\n};\n//类模板的实例化\ncao\u003Cint> c;\n//cao类名,cao\u003Cint>才是类型\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n四、非类型模板参数\n概念:非类型形参。就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用\n\n1.非类型的模板参数无法修改\n2.浮点数,类对象以及字符串无法做非类型模板参数\n(非类型模板参数一般是整形)\n3.非类型的模板参数必须在编译期间就能确认结果。\n\nN就是非类型形参。\n\ntemplate\u003Cclass T, size_t N = 10>\n1\n五、模板的特化\n使用模板可以实现与类型无关的代码。但有时候还需要做一些特殊处理。\n\n1.函数模板的特化\n特化步骤\n1.必须要先有一个基础的函数模板\n2.关键字template后面接一对空的尖括号\u003C>\n3.函数名后跟一对尖括号,尖括号中指定需要特化的类型。\n4.函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪错误\n\nstruct Date\n{\n\tint _year = 1;\n\tint _month = 1;\n\tint _day = 1;\n};\n//基础的函数模板\ntemplate\u003Cclass T>\nbool IsEqual(T left, T right)\n{\n\treturn left == right;\n}\n//关键字template后面接一对空的尖括号\u003C>\ntemplate\u003C>\n//函数名后跟一对尖括号,尖括号中指定需要特化的类型\n//函数形参表:必须要和模板函数的基础参数类型完全相同\nbool IsEqual\u003CDate*>(Date* left, Date* right)\n{\n\treturn left->_year == right->_year\n\t\t&& left->_month == right->_month\n\t\t&& left->_day == right->_day;\n}\n\nint main()\n{\n\tcout \u003C\u003C IsEqual(1, 2) \u003C\u003C endl;\n\n\tDate* p1 = new Date;\n\tDate* p2 = new Date;\n\tcout \u003C\u003C IsEqual(p1, p2) \u003C\u003C endl;\n\treturn 0;\n}\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n2.类模板的特化\n全特化\n全特化就指的是模板参数列表中所有的参数都确定化。\n\n//全特化\ntemplate\u003Cclass T1, class T2>\nclass Data\n{\npublic:\n\tData()\n\t{\n\t\tcout \u003C\u003C “Data\u003CT1, T2>” \u003C\u003C endl;\n\t}\nprivate:\n\tT1 _d1;\n\tT2 _d2;\n};\n\n//跟一对尖括号\u003C>\ntemplate\u003C>\nclass Data\u003Cint, char>\n{\npublic:\n\tData()\n\t{\n\t\tcout \u003C\u003C “Data\u003Cint,char>” \u003C\u003C endl;\n\t}\nprivate:\n\tint _d1;\n\tchar _d2;\n};\n\n\nint main()\n{\n\tData\u003Cint, int> d1;\n\t//模板的全特化\n\tData\u003Cint, char> d2;\n}\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n偏特化\n偏特化是针对模板参数进一步进行条件限制设计的特化版本。\n\n//类的基础模板\ntemplate\u003Cclass T1, class T2>\nclass Data\n{\npublic:\n\tData()\n\t{\n\t\tcout \u003C\u003C “Data\u003CT1, T2>” \u003C\u003C endl;\n\t}\nprivate:\n\tT1 _d1;\n\tT2 _d2;\n};\n\n//1.部分特化\n//将模板参数列表中的一部分参数特化。\n\ntemplate \u003Cclass T1>\nclass Data\u003CT1, int>\n{\npublic:\n\tData()\n\t{\n\t\tcout \u003C\u003C “Data\u003CT1, int>” \u003C\u003C endl;\n\t}\nprivate:\n\tT1 _d1;\n\tint _d2;\n};\n\n//两个参数偏特化为引用类型\ntemplate \u003Ctypename T1, typename T2>\nclass Data \u003CT1*, T2*>\n{\npublic:\n\tData() { cout \u003C\u003C “Data\u003CT1*, T2*>” \u003C\u003C endl; }\nprivate:\n\tT1 _d1;\n\tT2 _d2;\n};\n\n//两个参数偏特化为引用类型\ntemplate \u003Ctypename T1, typename T2>\nclass Data \u003CT1&, T2&>\n{\npublic:\n\tData(const T1& d1, const T2& d2)\n\t\t:_d1(d1)\n\t\t, _d2(d2)\n\t{\n\t\tcout \u003C\u003C “Data\u003CT1&, T2&>” \u003C\u003C endl;\n\t}\nprivate:\n\tconst T1& _d1;\n\tconst T2& _d2;\n};\n\nint main()\n{\n\tData\u003Cdouble, int> d1;\n\tData\u003Cint, double> d2;\n\tData\u003Cint*, int*> d3;\n\tData\u003Cint&, int&> d4(1,2);\n}\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n61\n62\n63\n64\n六、模板分离编译(重点)\n1.什么是分离编译 ?\n一个程序由若干个源文件组成。而每个源文件单独编译成目标文件,然后目标文件链接起来形成可执行文件的过程称为分离编译。\n\n模板一般不支持分离编译。但普通函数是可以的。\n\nC/C++程序要运行,基本步骤.\n预处理 -> 编译->汇编->链接\n\n2.模板的声明和定义\n函数模板的声明和定义也有一些讲就。\n声明:\n\ntemplate\u003Ctypename T>\nvoid Swap(T& left, T& right);\n1\n2\n定义:\n\ntemplate\u003Ctypename T>\nvoid Swap(T& left, T& right)\n{\n\tT temp = left;\n\tleft = right;\n\tright = temp;\n}\n1\n2\n3\n4\n5\n6\n7\n注意:类模板的定义还需要加上类域。\n\n3.模板的分离编译原理\na.cpp\n\na.h\n\nmain.cpp\n\n\na.h头文件不参与编译。\na.cpp中不会生成模板函数的实例化,\nmain.cpp中调用函数链接时找地址。main.cpp包含的头文件只有a.h的。但是a.cpp中没有实例化所以没有地址。\n\n解决方法:\n1.将声明和定义放到一个文件.hpp的文件中。\n2.模板定义的位置显式实例化。(不推荐使用).\n\n模板的优点:\n\n1.模板复用了代码,节省了资源。STL标准模板库因此而产生。\n2.增强了代码的灵活性。\n\n缺点:\n1.模板会导致代码膨胀,也会导致编译时间变长。\n2.出现模板编译错误时,错误信息非常凌乱,不易定位错误。\n\n为什么分离就链接不上?\n符号表找不到。\n和实例化有关系。\n\na.cpp 从预处理到 a.i经过了头文件的展开。\na.i经编译到a.s再经过汇编到a.o什么都没干,因为模板的类型没有确定,所以没法实例化。a.s和a.o都是空的,空壳子。\n\n解决办法:\n1.显示实例化,太矬了,几乎不用这个办法。\n2.不分离到两个文件中,放到同一个文件中。这样为什么就可以了呢?\n为什么就不存在链接错误了?**原因是因为在main.cpp中头文件展开后,有了函数模板的声明和定义。**在链接的时候就不用找他的地址了。