第24课 - 专题四经典问题解析
一.历史的痕迹
Source Example 1.1: #include <iostream> using namespace std; template<class T> T Minus(T a, T b) { return a - b; } /* 从结果看与普通的typename T,没有区别 */ template<class T> class Add{ public: T add(T a,T b) { return a + b; } }; int main(int argc, char** argv) { Add<double> a; /* 输出-1 */ cout<<Minus<int>(1,2)<<endl; /* 输出5 */ cout<<a.add(2,3)<<endl; return 0; } 结论:class也可以定义模板,但是为什么需要typename关键字? 1.2 在类中可以定义其他新类型 Source Example 1.2: #include <iostream> using namespace std; class Test { public: typedef int *PINT; struct Point { int x; int y; }; class Sub{ public: Sub() { cout<<"Sub()"<<endl; } void print() { printf("hello world!\n"); } }; }; int main(int argc, char** argv) { Test::PINT pi = new int(5); Test::Point po = {2,3}; Test::Sub sub; cout<<*pi<<endl; cout<<po.x<<" "<<po.y<<endl; sub.print(); delete pi; return 0; }
输出结果如下:
1.3 在类模板中定义新类型
#include <iostream> using namespace std; template <class T,int N> class Test { public: typedef T ElemType; enum { LEN = N }; T array[LEN]; }; int main(int argc, char** argv) { Test<int, 5> t1; Test<float, 3> t2; return 0; }
1.4 在函数模板中使用类模板的内部类型
#include <iostream> using namespace std; template <class T,int N> class Test { public: typedef T ElemType; enum { LEN = N }; T array[LEN]; }; template<class T> /* 编译报错,T::ElemType不是一个类型 */ /* ElemType应该是一个类的内部类型还是类的静态成员变量,具有二义性 */ /* 编译器会默认为是一个静态成员变量 */ /* 加上typename表示后面的是一个类型,编译就会通过 */ void test_copy(T& test,typename T::ElemType a[], int len) { int l = (len < T::LEN) ? len : T::LEN; for (int i; i < l;i++) { test.array[i] = a[i]; } } int main(int argc, char** argv) { Test<int, 5> t1; Test<float, 3> t2; int ai[] = {5,4,3,2,1}; float af[] = {0.1,0.2,0.3}; test_copy(t1, ai, 6); return 0; }
1.4 为什么class可以用来定义模板参数呢?
为什么还要仅仅typename呢?
1.4.1 模板最初的目标指示为了对类类型进行泛型操作的定义,因此用class关键字声明泛型类型。
1.4.2 在之后的进化过程中发现了模板相互调用产生::操作符的二义性
1.4.3 因此引入typename关键字是用于高速编译器将::符号后的标识符看作是类型
二.坑爹的面试题
2.1 写一个函数判断一个变量是否为指针?
2.2拾遗:
2.2.1 C++中仍然支持C语言中可变参数函数
2.2.2 C++编译器的匹配调用优先级
a.重载函数
b.函数模板
c.可变参数函数
Source Example 2.2.2: #include <iostream> using namespace std; int Test(int i, int j) { cout<<"int Test(int i, int j) "<<endl; } template<typename T> T Test(T i, T j) { cout<<"T Test(T i, T j) "<<endl; } int Test(...) { cout<<"int Test(...) "<<endl; } int main(int argc, char** argv) { int i, j = 0; /* 优先选择重载函数 */ Test (i, j); /* 最后匹配可变参数函数 */ Test (i, j, 0); return 0; }
2.3 函数模板与可变参数函数的化学反应
Source Example 2.3(判断指针的解决方案1): #include <iostream> using namespace std; template<typename T> void isPtr(T*) { cout<<"is a ptr"<<endl; } void isPtr(...) { cout<<"not is a ptr"<<endl; } int main(int argc, char** argv) { int *pi = NULL; float* pf = NULL; int i = 0; int j = 0; isPtr(pi); isPtr(pf); isPtr(i); isPtr(j); return 0; }
输出结果如下:
这个方法不够高效,在函数调用的进栈与退栈比较耗
Source Example 2.4(判断指针的解决方案2): #include <iostream> using namespace std; template<typename T> char isPtr(T*); int isPtr(...); /* sizeof关键字在编译期间大小就确定好了 */ #define ISPTR(v) (sizeof(isPtr(v)) == sizeof(char)) int main(int argc, char** argv) { int i = 0; int *p = NULL; cout<<ISPTR(i)<<endl; cout<<ISPTR(p)<<endl; cout<<ISPTR(1)<<endl; cout<<ISPTR("asfda")<<endl; cout<<ISPTR(0.5)<<endl; return 0; }