第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;
}