专题四经典问题解析

历史的痕迹

你可能会看到类似下面的模板定义

#include <cstdlib>
#include <iostream>

using namespace std;

template<class T> //此处用class?
T Minus(T a, T b)
{
    return a - b;
}

template<class T>
class Add
{
public:
    T add(T a, T b)
    {
        return a + b;
    }
};

int main(int argc, char *argv[])
{
    cout<<Minus(3, 4)<<endl;
    cout<<Minus<float>(0.3, 0.4)<<endl;
    
    Add<double> ap;
    
    cout<<ap.add(9, 8)<<endl;
    cout<<ap.add(0.001, 0.1)<<endl;
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FehMj7KI-1584946686811)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584943943803.png)]

为什么class可以用来定义模板参数呢?
为什么还要引进typename呢?

①.小插曲:在类中可以定义其它的新类型

#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"

using namespace std;

//类某种程度上可以看作是命名空间
//所以类内部可以定义其他的数据类型

class Test{
public:
    typedef int* PINT ;

    struct Point{
        int x;
        int y;
    };

    class Sub{
    public:
        Sub(){
            cout << "Sub()" <<endl;
        }

        void print(){
            cout << "Hello World" <<endl;
        }
    };
};

int main(int argc, char *argv[])
{
    Test::PINT p = new int(5);
    Test::Point po = {2, 3};
    Test::Sub sub;

    cout << *p <<endl;
    cout <<po.x << " " <<po.y <<endl;
    sub.print();

    delete p;

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1DQlo6y-1584946686814)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584928877999.png)]

②.在类模板中可以定义新的类型

#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"

using namespace std;

//在类模板中也可以 定义新的类型

template<class T, int N>
class Test{
public:
    typedef T ElemType;

    enum {
        LEN = N
    };

    ElemType array[LEN];
};

int main(int argc, char *argv[])
{
    Test<int, 5> t1;
    Test<float, 3> t2;

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAcEE9FU-1584946686817)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584929494636.png)]

③.在函数模板中 使用类模板的内部类型

#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"

using namespace std;

//在类模板中也可以 定义新的类型

template<class T, int N>
class Test{
public:
    typedef T ElemType;

    enum {
        LEN = N
    };

    ElemType array[LEN];
};

//函数模板
template<class T>
void test_copy(T& test, T::ElemType a[], int len) //只看 T::ElemType, 到底是 静态成员变量、还是 类中的类型
                                                  //此处有二义性
                                                  //编译器 默认为静态成员变量
    											  //typename 告诉编译器 后面的T::ElemType 是一个类型
{
    int l = len < T::LEN ? len : T::LEN; //数组:取较小的长度

    for(int i=0; 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,0};
    float af[] = {0.1, 0.2, 0.3};

    test_copy(t1, ai, 6);


    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IKk8pxjQ-1584946686819)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584932937422.png)]

④.改为typename

完整代码

#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"

using namespace std;

//在类模板中也可以 定义新的类型

template<typename T, int N> //class真的不合适
class Test{
public:
    typedef T ElemType;

    enum {
        LEN = N
    };

    ElemType array[LEN];
};

//函数模板
template<typename T>
void test_copy(T& test, typename T::ElemType a[], int len) //只看 T::ElemType, 到底是 静态成员变量、 静态成员函数、还是 类中的类型
                                                  //此处有二义性
                                                  //编译器 默认为静态成员变量
{
    int l = len < T::LEN ? len : T::LEN; //数组:取较小的长度

    for(int i=0; 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,0};
    float af[] = {0.1, 0.2, 0.3};

    test_copy(t1, ai, 6);
    for(int i=0; i<t1.LEN; i++){
        cout << t1.array[i] <<endl;
    }

    cout << endl;

    test_copy(t2, af, 6);
    for(int i=0; i<t2.LEN; i++){
        cout << t2.array[i] <<endl;
    }

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wygfhTEX-1584946686822)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584933779678.png)]

为什么class可以用来定义模板参数呢?
为什么还要引进typename呢?

​ 模板最初的目标只是为了对类类型进行泛型操作的定义,因此用class关键字声明泛型类型
​ 在之后的进化过程中发现了模板相互调用时产生的 :: 操作符的二义性
​ 因此引入typename关键字是用于告诉编译器将 :: 符号后的标识符看作类型

坑爹的面试题

面试官:你的简历上写着你熟悉C++,那么你写函数判断一个变量是否为指针吗?
应聘者:。。。

小插曲

拾遗
C++中仍然支持C语言中的可变参数函数
C++编译器的匹配调用优先级
重载函数
函数模板
可变参数函数

①.C++编译器匹配示例

#include <cstdlib>
#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 = 0;
    int j = 0;

    test(i, j);

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Gtq6FAw-1584946686824)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584945148310.png)]

②.函数模板与 可变参数函数 的化学反应

#include <cstdlib>
#include <iostream>

using namespace std;

template<typename T>
void isPtr(T*)
{
    cout << "void isPtr(T*)" <<endl;
}

void isPtr(...){
    cout << "void isPtr(...)" <<endl;
}

int main(int argc, char *argv[])
{
    int* pi = NULL;
    float* pf = NULL;
    int i = 0;
    float f = 0.0f;

    isPtr(pi);
    isPtr(pf);
    isPtr(i);
    isPtr(f);

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQVppTN4-1584946686826)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584945534675.png)]

①.解决方案一

#include <cstdlib>
#include <iostream>

using namespace std;

template<typename T>
bool isPtr(T*)
{
    return true;
}

bool isPtr(...){
    return false;
}

int main(int argc, char *argv[])
{
    int* pi = NULL;
    float* pf = NULL;
    int i = 0;
    float f = 0.0f;

    cout << isPtr(pi) <<endl;
    cout << isPtr(pf) <<endl;;
    cout <<isPtr(i) <<endl;
    cout <<isPtr(f)<<endl;

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SOxhP8BP-1584946686853)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584945735791.png)]

面试官:你的方法实现了指针的判断,但是我觉得不够高效,你有更好的办法吗?
应聘者:。。。

分析:
解决方案1已经很好的解决面试官的问题,那么为什么还不够高效呢?哪里不够高效呢?

tips:

解决方案1中的唯一耗时的地方在于函数调用的建栈与退栈过程,因此需要考虑如何避免这个过程以提高程序效率。

②.解决方案二

#include <cstdlib>
#include <iostream>

using namespace std;

template<typename T>
char isPtr(T*);

int isPtr(...);

#define ISPTR(v) (sizeof(isPtr(v)) == sizeof(char) ) //不需要函数调用开销,比方案一 高效

int main(int argc, char *argv[])
{
    int* pi = NULL;
    float* pf = NULL;
    int i = 0;
    float f = 0.0f;

    cout << ISPTR(pi) <<endl;
    cout << ISPTR(pf) <<endl;;
    cout << ISPTR(i) <<endl;
    cout << ISPTR(f)<<endl;

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GR9gSj9U-1584946686855)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1584946305213.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值