第24课 - 专题四经典问题解析

1. 历史的痕迹

#include <cstdlib>

#include <iostream>

using namespace std;

template<class T>  //以前是用typename定义,现在是用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;

}

运行结果:

-1

-0.1

17

0.101

对于上面的程序,class可以用来定义模板参数,为什么还有引进typename呢?

在有C++的时候,泛型还没有广泛的使用。最早的时候typename还没有使用,知道泛型广泛应用的时候,才引进泛型。我们看下面的分析。

在类中可以定义其它的新类型

#include <cstdlib>

#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()

{

cout<<"Hello World"<<endl;

}

};

};

int main(int argc, char *argv[])

{

Test::PINT pi = new int(5);

Test::Point po = {2, 3};

Test::Sub sub; //可以像使用普通的类类型一样使用内部类

cout<<*pi<<endl;  //打印pi这个指针指向的空间的值

cout<<po.x<<" "<<po.y<<endl;

sub.print();

delete pi;  //与new对应,必须释放

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

运行结果:

Sub()

5

2 3

Hello World

我们看到普通的类中可以定义新的类,那么类模板中肯定也能,我们看下面。

在类模板中定义新的类型

template<class T, int N>

class Test

{

public:

typedef T ElemType;  //将ElemType定义为T

enum { LEN = N };

T array[LEN];

};

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

#include <cstdlib>

#include <iostream>

using namespace std;

template<typename T, int N>

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)  /*模板函数进行复制,此时的ElemType就是是内部的类型还是成员函数,有二义性,编译器无法确定T是什么,编译器就会默认这是一个静态成员变量,变量名后跟数组,就是不合理的,于是我们就出现了typename,这样就合理了。所以那么我们在应用的开始,直接就把class变成typename就好了,这样就使得程序更好理解。就像我们的class和struct都可以定义类,但是我们在C++中会使用class。这样就会解决我们编译器之间的兼容性。*/

{

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);

test_copy(t2, af, 3);

for(int i=0; i<5; i++)

{

cout<<t1.array[i]<<endl;

}

for(int i=0; i<Test<float, 3>::LEN; i++)

{

cout<<t2.array[i]<<endl;

}

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

运行结果:

5

4

3

2

1

0.1

0.2

0.3

模板最初的目标只是为了对类类型进行泛型操作的定义,因此用class关键字声明泛型类型。

在之后的进化过程中发现了模板相互调用时产生的::操作符的二义性。

因此引入typename关键字是用于告诉编译器将::符号后的标识符看作类型。

2. 坑爹的面试题

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

C++中仍然支持C语言中的可变参数函数,C++编译器的匹配调用优先级:

(1)重载函数

(2)函数模板

(3)可变参数函数

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;

}

运行结果:

int test(int i, int j)

函数模板与可变参数函数的化学变化

#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;

int j = 0;

isPtr(pi);

isPtr(pf);

isPtr(i);

isPtr(j);

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

运行结果:

void isPtr(T*)

void isPtr(T*)

void isPtr(...)

void isPtr(...)

我们写一个函数模板,只能匹配指针参数,当这个函数被选中就说明我们定义的是指针,不被引用,说明我们用的是常量。

 

解决方案1

template<typename T>

bool isPtr(T*)

{

return true;

}

bool isPtr(...)

{

return false;

}

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

分析

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

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

解决方案2

template<typename T>

char isPtr(T*);   //没有写函数体,就不用调用了

int isPtr(...);     //没有写函数体,就不用调用了

#define ISPTR(v) (sizeof(isPtr(v)) == sizeof(char))

/*sizeof在定义的时候就知道大小,即使不会被调用,在编译的时候就会被调用。当v是指针的时候,选择函数模板,返回值是char类型,大小与sizeof(char)一致,显示为一。当v不是指针的时候,调用可变参数函数,返回值为int,与后面的值不等。*/

int main(int argc, char *argv[])

{

int* pi = NULL;

float* pf = NULL;

int i = 0;

int j = 0;

cout<<ISPTR(pi)<<endl;

cout<<ISPTR(pf)<<endl;

cout<<ISPTR(i)<<endl;

cout<<ISPTR(j)<<endl;

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

 

转载于:https://www.cnblogs.com/free-1122/p/11336299.html

C++--第24课 - 专题四经典问题解析相关推荐

  1. 计算机信息继续考试 d组教材,无为县“备好课”专题培训考核小结

    无为县2008--2009学年度"备好课"专题培训,注重能力考核,过程性考查和结果性考核相结合,有效地调动了参训教师远程学习的主动性,促进了集体备课制度的建设和校本教研文化建设. ...

  2. Swift解读专题四——字符串与字符

    2019独角兽企业重金招聘Python工程师标准>>> Swift解读专题四--字符串与字符 一.引言 Swift中提供了String类型与Characters类型来处理字符串和字符 ...

  3. 果园机器人的写作思路_《24课果园机器人》 教学设计 1课时

    三年级 第十三周12年5月3日 星期四 24 课果园机器人 [教材简说] 这是一篇能体现电脑神奇的文章,所写的内容极为有趣.文章中的机器人服务于丰收的果园中,它们能把成熟的果子从树上摘下来,整齐地装箱 ...

  4. 欧姆定律的c语言编程例题,欧姆定律计算题专题训练(经典全面附答案).doc

    欧姆定律计算题专题训练(经典全面附答案) 欧姆定律计算题专题训练 1.如图所示的电路中,电压表V1的示数为9伏,电压表V2的示数为3伏,那么R1与R2的阻值之比为 A.2:1 B.1:2 C.3:1 ...

  5. 软考复习专题四---多媒体专题

    专题四:多媒体专题 1.1多媒体知识概述 1.基本概念 多媒体信息一般指用文本.图形.图像.动画.音频和视频影像等形式表示的信息. 多媒体计算机技术是指利用计算机交互地综合处理相互之间有联系的文本.图 ...

  6. 免费技术直播:唐宇迪带你一节课了解机器学习经典算法

    常常有小伙伴在后台反馈:机器学习经典算法有哪些? 自学难度大又没有效果,该怎么办? CSDN为了解决这个难题,联合唐宇迪老师为大家带来了一场精彩的直播[一节课掌握机器学习经典算法-线性回归模型].本次 ...

  7. 解题报告:【kuangbin带你飞】专题四 最短路练习题

    目录 A. POJ - 2387 TiltheCowsComeHomeTil\ the\ Cows\ Come\ HomeTil the Cows Come Home--------(最短路模板题)[ ...

  8. 【阿里云总监课第四期】时髦的云原生应用怎么写?

    为什么80%的码农都做不了架构师?>>>    概述 应用已经跨入了云原生的时代.要写一个时髦的云原生应用,首先当然要了解什么是云原生.CNCF,也就是云原生计算基金会,作为目前人气 ...

  9. [ 浙江大学 程序设计专题 ] 四个专题代码 报告 PPT共享

    [原创]转载请注明出处,请勿用于作弊 专题一: 链接: https://pan.baidu.com/s/11xCwvuPHDkTPeOB_yzJWnw 提取码: prup 专题二: 链接: https ...

  10. 开发指南专题四:JEECG微云快速开发平台--JEECG开发环境搭建

    开发指南专题四:JEECG微云快速开发平台开发环境搭建 1. JEECG开发环境搭建 JEECG推荐的开发环境为Myeclipse8.5/Eclipse3.7+JDK1.6+Tomcat6.0 1.1 ...

最新文章

  1. org.quartz-scheduler 基础过程
  2. 开课吧python学费-开课吧成为CNCC中国计算机大会唯一教育合作伙伴
  3. PL/SQL 数据库连接工具的下载、安装与使用实例演示
  4. Linux 标准I/O函数库
  5. open source protocols
  6. 【渝粤题库】国家开放大学2021春2717家畜解剖基础题目
  7. 在搜索框自动输入文本_【Zotero文档翻译】管理篇:搜索
  8. 两个线程能在cpu中同时运行吗_多核和多线程那些事
  9. win11更新特别慢怎么办 windows11更新缓慢的解决方法
  10. Hadoop架构中各个集群在开发中的作用
  11. nginx proxy_pass和rewrite
  12. Jmeter下载及安装方法
  13. DirectShow 简介
  14. 如何安装.ipa文件
  15. 在线广告结算方式及对比
  16. qcloud.login 登录失败,可能是网络错误或者服务器发生异常的多种解决方法
  17. 读书寄语:难过时就抱抱自己,时间治愈的,都是愿意自渡的人
  18. ISCC2018 Misc WriteUp
  19. Invalid attempt to spread non-iterable instance错误
  20. 场强和电阻_场强是标量还是矢量 如何判断

热门文章

  1. RxJava学习总结
  2. java paint绘图添加组件不能显示_java – 为什么paintComponent没有在面板上绘图?
  3. 计算机二级c语言编译题评分,计算机二级C语言题型和评分标准
  4. Linux 之CentOS7使用firewalld打开关闭防火墙与端口
  5. postgis java_Postgresql结合postgis使用java的JDBC连接
  6. java蘑菇岛种子_我的世界:Java版玩家笑开了怀,4个地图种子,2个内陆蘑菇岛...
  7. ubuntu 安装 mysql debug_ubuntu 安装phpstorm+xdebug 动态调试环境
  8. 更新日志_CargoWare系统云平台更新日志2020.11.24
  9. Mysql和mono_c# – 让Linq与Mysql和Mono玩得很好,有可能吗?
  10. linux计划任务 没30s_一分钟学会电脑Linux系统和Windows另类的关机方法,6到飞起来...