初识C++模板

1 引入模板

        实现一个算法:返回两个变量中的较大的一个,并支持不同的数据类型。如果不使用C++模板,会有哪些选择呢?

        根据数据类型一遍一遍实现算法

int max_int(int x, int y) {return x > y ? x : y;
}int max_double(double x, double y) {return x > y ? x : y;
}

        使用void *指针,然后指定比较函数

typedef const void *(*cmp)(const void *, const void *);const void *max_cmp(const void *x, const void *y, cmp f)
{return f(x, y);
}const void *cmp_int(const void *x, const void *y) {const int *ix = (const int *)x;const int *iy = (const int *)y;return *ix > *iy ? x : y;
}const void *cmp_double(const void *x, const void *y) {const double *dx = (const double *)x;const double *dy = (const double *)y;return *dx > *dy ? x : y;
}

        使用继承,定义一个接口类,由派生类实现比较函数

class base
{
public:virtual bool max_than(const base &rhs) const = 0;
};class I : public base {
public:I(int i) : m_data(i) {}virtual bool max_than(const base &rhs) const {const I &other = dynamic_cast<const I &>(rhs);return m_data > other.m_data;}public:int m_data;
};class D : public base {
public:D(int d) : m_data(d) {}virtual bool max_than(const base &rhs) const {const D &other = dynamic_cast<const D &>(rhs);return m_data > other.m_data;}public:double m_data;
};const base &max_class(const base &x, const base &y) {return x.max_than(y) ? x : y;
}

        仔细观察,会发现三种方案都存在不同程度的冗余。

        方案一冗余程度最高,没有任何复用可言。这种方案不仅增加了工作量,还增加了维护成本。每当需要支持一种新的数据类型,都需要重新实现一遍算法;如果算法更新,还需要同步到各个实现。

        方案二和方案三的本质是一样的,对原有数据类型进行封装,通过回调方式解决数据类型差异问题。不同之处在于方案二使用void指针实现,存在严重的类型安全问题(但这也是C语言中的唯一选择);方案使用继承,通过基类引用取代了void指针。

        通过与上面这些方案对比,使用模板实现这个算法就简单的多了,如下:

#include <cstdio>
#include <string>
#include <string>template <typename T>
T max_(T x, T y)
{return x > y ? x : y;
}int main(int argc, char **argv)
{printf("%d\n", max_(10, 20));printf("%f\n", max_(10.1, 20.2));std::string s1("a");std::string s2("b");printf("%s\n", max_(s1, s2).c_str());return 0;
}

2 模板的本质 

         模板的本质是一种c++多态,又称编译时多态。尽管源码中仅有一个函数,但却实现了对多种类型的支持。为什么会有如此神奇的事情?因为C++编译器有一个神奇的技能——无中生有。通过“objdump --syms template | grep max_”查看可执行程序的符号表,程序中确实包含三个名称包含max_的函数,如下:

        可执行程序中的三个函数,与源码中的max_函数是什么关系呢?不妨做个实验。

        实验一数据类型为double的调用实例,发现可执行程序中名称包含max_的函数少了一个;

#include <cstdio>
#include <string>
#include <string>template <typename T>
T max_(T x, T y)
{return x > y ? x : y;
}int main(int argc, char **argv)
{printf("%d\n", max_(10, 20));std::string s1("a");std::string s2("b");printf("%s\n", max_(s1, s2).c_str());return 0;
}

        增加一行数据类型为int的调用实例,发现可执行程序中名称包含max_的函数并未增加;

#include <cstdio>
#include <string>
#include <string>template <typename T>
T max_(T x, T y)
{return x > y ? x : y;
}int main(int argc, char **argv)
{printf("%d\n", max_(10, 20));printf("%d\n", max_(50, 10));std::string s1("a");std::string s2("b");printf("%s\n", max_(s1, s2).c_str());return 0;
}

        从上面的实验推出,名称中包含max_的函数的生成,仅与实例的类型相关,与调用次数无关。但现在还不能推出可执行程序名称包含max_函数,就是源码中的max_函数。

        再做一个实验,使用“g++ -S template.cpp -o template.s”生成汇编代码,仔细研究template.s(代码太长,此处不在粘贴),发现可执行程序名称包含max_函数,就是源码中的max_函数。在代码的编译阶段,g++为max生成了三个不同的实例版本。

        由此可见,模板也并非免费的午餐:增加编译时间;增加生成程序的体积。但与其所提升的开发效率相比,这些可以忽略不计。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/413218.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系统编程】环境变量的组织方式

environ和getenv函数 在Linux中&#xff0c;environ是一个全局的外部变量&#xff0c;其类型为char**&#xff0c;存储着系统的环境变量。除了使用主函数中的第三个参数外&#xff0c;我们也可使用environ函数直接访问系统的环境变量。 注意&#xff1a;这里在代码内部使用envi…

[labelme]labelme如何将标注的json格式转成png的mask文件掩码文件

labelme工具不仅仅具有标注功能&#xff0c;而且可以将json文件转化为png的分割训练文件&#xff0c;如果您是一个类别则可以直接用labelme_json_to_dataset进行转换最后提取对应的掩码文件即可进行语义分割训练。如果您是>2个类别则不推荐使用labelme工具进行转换&#xff…

Liunx:线程控制

目录 创建线程&#xff1a;pthread_create(); 线程等待&#xff1a;pthread_join(); 线程退出&#xff1a;pthread_exit(); 线程取消&#xff1a;pthread_cancel() 说线程的时候说过&#xff0c;liunx没有选择单独定义线程的数据结构和适配算法&#xff0c;而是用轻量级进程…

软件测试|深入了解Python中的super()函数用法

简介 Python中的super()函数是一种强大的工具&#xff0c;用于在子类中调用父类的方法。它在面向对象编程中非常有用&#xff0c;可以让你轻松地扩展和重用现有的类。本文将详细介绍super()函数的用法&#xff0c;并提供一些示例来帮助你更好地理解它的功能。 什么是super()函…

el-date-picker如果超过限制跨度则提示

需求&#xff1a;实现日期时间选择组件跨度如果超过限制天数&#xff0c;点击查询则提示超过限制时间 封装一个方法&#xff0c;传入开始和结束时间以及限制天数&#xff0c;如果超过则返回false //计算时间跨度是否超过限制天数isTimeSpanWithinLimit(startTime, endTime, li…

Firefox 100 正式发布

五月三日&#xff0c;Firefox发布了它的第100个版本&#xff0c;来回顾一下Firefox是如何走到今天这一步的&#xff0c;以及在第100个版本中发布了哪些功能。 回顾 2004年&#xff0c;《纽约时报》上宣布了Firefox 1.0的发布&#xff0c;这个广告列出了为第一版做出贡献的每一…

Seaborn可视化的各种图及代码演示

一.简介 Seaborn是基于matplotlib的图形可视化python包。它提供了一种高度交互式界面&#xff0c;便于用户能够做出各种有吸引力的统计图表。 Seaborn是在matplotlib的基础上进行了更高级的API封装&#xff0c;从而使得作图更加容易&#xff0c;在大多数情况下使用seaborn能做…

Modbus协议学习第三篇之协议通信规则

导语 本篇博客将深入介绍Modbus协议的一些内容&#xff0c;主要包括通讯方式和通讯模型的介绍 Modbus通讯方式 Modbus协议是单主机、多从机的通信协议&#xff0c;即同一时间&#xff0c;总线上只能有一个主设备&#xff0c;但可以有一个或者多个从设备&#xff08;最多好像是2…

SpringMVC下半篇之异常处理器及日期转换器

3.异常处理器 如果不加以异常处理&#xff0c;错误信息肯定会抛在浏览器页面上&#xff0c;这样很不友好&#xff0c;所以必须进行异常处理。 3.1.异常处理思路 系统的dao、service、controller出现都通过throws Exception向上抛出&#xff0c;最后由springmvc前端控制器交由…

图像处理中,采用极线约束准则来约束特征点匹配搜索空间,理论上在极线上进行搜索。这里的极线是什么线,怎么定义的?基本矩阵F和本质矩阵E有什么区别?

问题描述&#xff1a;图像处理中&#xff0c;采用极线约束准则来约束特征点匹配搜索空间&#xff0c;理论上在极线上进行搜索。这里的极线是什么线&#xff0c;怎么定义的&#xff1f;基本矩阵F和本质矩阵E有什么区别&#xff1f; 问题1解答&#xff1a; 极线是通过极线几何学…

银行家算法

文章目录 主要内容一.银行家算法1.需求分析 2.概要设计3.源代码代码如下&#xff08;示例&#xff09;: 总结 主要内容 一.银行家算法 1.需求分析 通过编写和调试一个系统动态分配资源的简单模拟程序&#xff0c;观察死锁产生条件&#xff0c;采用适当的算法&#xff0c;有效…

Discuz论坛网站登录账号操作慢,必须强制刷新才会显示登录怎么办?

飞飞发现在登录服务器大本营账号时&#xff0c;输入账号密码登录后还是显示的登录框&#xff0c;强制刷新后才知道已经登录了&#xff0c;每次都要刷新才能正常显示&#xff0c;非常影响用户体验&#xff0c;于是在网上找了类似的问题故障解决方法&#xff0c;目前问题已经解决…