C++ 深入理解模板实现多态思想
创始人
2024-05-22 18:39:24
0

文章目录

  • 前言
  • 一、模板与多态基础
    • 1.模板
    • 2.多态
  • 二、模板实现多态
  • 三、实际应用


前言

对C/C++学习感兴趣的可以看看这篇文章噢:C/C++教程

最近有时间,便用WTL写了一个兼具群聊单聊以及传输文件的聊天软件,过几天应该就能更新到 C/C++教程系列 中了

所以在这里提前讲解一下WTL中的一个非常重要的概念:模板实现多态

一、模板与多态基础

再进一步了解如何用模板来实现多态前,我们还是来看一看这两个概念的基础理解

1.模板

首先是模板,其主要用途在于让我们程序员少写代码

比如像下面两个函数类似的一系列函数:

int add(int a, int b) {return a + b;
}
double add(double a, double b) {return a + b;
}

就可以用模板简写为:

template
T add(T a, T b) {return a + b;
}

使用的方式如下:

	add(1,3);add(1.1, 3.4);add('s','a');

但由于C++编译器可以自动推断参数类型,所以中间的是可以省略的

这里要注意一个非常重要的问题,虽然我们只写了一个模板函数,但实际上并不止有一个函数

比如这里我们用了三种类型的add函数,那么编译器就会为我们分别生成三个函数

也就是说,这是编译器根据我们写的模板。帮我们自动生成的函数

进一步来说,模板是完全给编译器看的,并不会参与到最终的可执行文件中

上面这一点便是模板的精髓!

为了更加直观的理解,我们来看一下最终生成的三个函数的内存地址:

#include
template
T add(T a, T b) {return a + b;
}int main() {printf("%p\n", add);printf("%p\n", add);printf("%p\n", add);
}

这样我们就能实际的看到最终确实是生成了三个函数,因为三个函数的地址完全不同,分别就代表着三个版本的add函数
在这里插入图片描述
总结来说就是,模板并没有减少最终的代码量,它仅仅只是减少了我们程序员需要写的代码量

并且这个过程是在编译期间就完成了的,这一点很重要!

之所以要用模板来实现多态,就是看重了它是在编译期间就完成的,而不会去影响最终的可执行文件的执行时间、大小

2.多态

然后便是多态了,多态是类中一个很重要的概念,其主要用途就是使得函数接口统一化

比如下面这段代码:

#include  
using namespace std;
class A {
public:virtual void area() {cout << "这是基类A" << endl;}
};
class B : public A {
public:void area(){cout << "这是子类B" << endl;}
};
class C : public A {
public:void area(){cout << "这是子类C" << endl;}
};
// 程序的主函数
int main()
{A* a;a = new B();a->area();a = new C();a->area();return 0;
}

逻辑并不复杂,就是B,C两个类都继承于A

并且在基类中我们用到了关键字virtual 定义area为虚函数,还在两个子类里面都分别重写了这个函数

因为BC类都继承于A类,所以我们可以用A类指针来接收BC对象

从占用内存上考虑,子类是继承父类的,所以子类所占用的内存量肯定大于或等于父类占用内存,那么子类申请一块内存,赋值给父类的指针,父类就不可能会内存访问越界,而反过来,如果用子类指针存储父类对象,由于子类访问的内存大于等于父类,就可能造成内存访问越界,因此一般禁止这样使用

此时我们发现,我们只用了一个A调用同一个函数area,却可以完成两个类的调用!

在这里插入图片描述

所以很多时候,当我们使用别人的提供给我们的类时,只要知道了它的父类有哪些函数,那么其子类就必然有对应的函数

这可以极大方便类的管理、升级以及使用

虽然它的好处很多,但同样也有坏处,那就是它是动态绑定函数的,依靠了一个叫做虚函数表的东西,导致其内存占用更大,运行时间更长

比如上面的代码我们就可以在调试窗口中看到其虚函数表:

在这里插入图片描述

就是这个名为 _vfptr的变量名称,他就是指向虚函数表的函数指针,而虚函数表中就存有我们的虚函数

父类指针想要正确使用子类重写的函数,就必须要在这个虚函数表中进行遍历查询对应的函数地址

所以一旦你的类中有虚函数,那么你的类就肯定会多出一个指针大小的内存用于存储虚函数表的地址,并且最终生成的可执行文件也会变大很多字节

这取决于你的虚函数个数,每多一个虚函数,那么虚函数表就需要多一个指针大小的内存来存储

如果依旧不太懂的,可以自行在浏览器中搜索一下,有很多优秀的文章对此有解释

总结来说就是:使用传统类的多态特性,会导致程序效率变低,最终生成的可执行文件体积变大

原因就是它生成了虚函数表、虚函数指针,在程序运行过程中执行查询函数的操作

MFC就是因为大量使用的这种多态,公共控件都继承于基本窗口类,一般都有数十上百个虚函数,所以这就导致即使你什么都没干,一个MFC程序都至少有数兆大小,并且运行效率还较低

二、模板实现多态

了解了上面所说的两个基本概念的优缺点之后,现在我们就可以来到如何使用模板来实现多态了

因为模板就是编译期间就完成的操作,如果让模板来实现多态,那么就不存在运行期间去遍历虚函数表来找对应的函数,也不需要开辟一个虚函数表来存储虚函数地址

既能节约内存,又能提高程序运行效率,是不是非常的完美!

下面我们就来看一看模板实现多态的基本流程

#include  
using namespace std;
template
class A {
public:void Show() {T* p=static_cast(this);p->area();}void area() {cout << "这是基类A" << endl;}
};
class B : public A {
public:void area(){cout << "这是子类B" << endl;}
};
class C : public A {
public:void area(){cout << "这是子类C" << endl;}
};
// 程序的主函数
int main()
{B b;b.Show();C c;c.Show();return 0;
}

这里同样是B,C两个类都继承自A类,但不同点就在于A类带了一个模板变量

所以B,C类在继承A的时候,就需要将自己这个类型传递进去

此时三个类都写了area函数,但只有基类写了show方法对吧

但由于B,C类都是继承自A类,所以它们其实也已经含有了show方法

然后便是最重要的一步,在基类的show方法中,我将this指针转化为T类型指针

static_cast与强制转化基本等价,唯一很大一点的区别就是,强制转换可以任意使用,比如B没有继承自A类,强制转换仍然可以将两者指针进行转换,而static_cast无法转换两个毫不相干的东西,这样就保证了传入的类型是继承自基类的,否则编译会直接报错

此时这里的p指针

T* p=static_cast(this);

实际就转化为了调用者的指针,以B举例子:

B b;
b.Show(); //调用Show方法后,完成了指针的转换,指代的B,那么B调用area函数,也就是调用自己重写的area函数

如果现在再多出一个子类D继承于A,但里面什么都没有:

class D : A{
}

那么当你使用D时:

D d;
d.Show(); //将指针转换为D类型,由于D类型没有重写area方法,所以将调用继承下来的基类area方法

同样是一个show函数,能够却能根据情况选择出不同的函数调用,而且还是在编译期间就完成了的

这便是模板实现多态的基本原理

在这里插入图片描述

三、实际应用

由于上面说的都是实现原理,例子比较奇怪,下面我们来直接看一看ATL中的代码:

WTL是基于ATL之上开发的,而ATL库则是vs开发环境中自带,WTL库需要自己去下载

可以输入以下代码

#include
class MyWindow :public CWindowImpl 
{};

然后右键速览CWindowImpl类,接着在跳出的文件中搜索static_cast:

在这里插入图片描述
就能看到很多像上图这样的调用

  1. 先将this指针还原为子类
  2. 然后再调用对应的函数
  3. 如果子类重写了这个函数,那么就调用子类的函数,否则就调用父类的函数

当然这并不完全如此,比如上图中的那一出,是将其转化为子类后,传给某个函数进行处理

不过总体逻辑是一致的:在父类中操作子类,以实现静态多态的目的

相关内容

热门资讯

毕业晚会致辞 毕业晚会致辞(精选18篇)  在学习、工作或生活中,大家都写过致辞吧,致辞要求风格的雅、俗、庄、谐要...
幼儿园六一节目串词 幼儿园六一节目串词红黄蓝幼第一文库网儿园节目串词主持人(师):亲爱的家长朋友们( ):敬爱的老师们(...
祝寿主持词 祝寿主持词  主持词要尽量增加文化内涵、寓教于乐,不断提高观众的文化知识和素养。在人们积极参与各种活...
回门宴主持词 让你的回门宴顺... 回门宴主持词 让你的回门宴顺利完成  篇一:新婚回门宴主持词  亲爱的各位来宾,各位亲朋好友,先生们...
结婚新郎致辞 结婚新郎致辞(15篇)  在学习、工作、生活中,说到致辞,大家肯定都不陌生吧,致辞具有能伸能缩,可以...
庆中秋迎国庆联欢晚会主持词(...   (念8条短信)  男:我们的驻外营销健儿发来的每一条祝福都是那么的感人。由于时间关系,我们不能一...
重庆森林经典台词 重庆森林经典台词  《重庆森林》由两个基本不相干的故事构成。两个故事之间的关系,就像擦身而过的金城武...
歌曲奔跑主持词串词 歌曲奔跑主持词串词范文  借鉴诗词和散文诗是主持词的一种写作手法。在如今这个中国,主持成为很多活动不...
年会老板感谢员工精彩致辞 年会老板感谢员工精彩致辞(通用16篇)  在日复一日的学习、工作或生活中,大家都用到过致辞吧,致辞具...
六一文艺汇演主持词 六一文艺汇演主持词(通用10篇)  主持词要根据活动对象的不同去设置不同的主持词。在人们积极参与各种...
文艺晚会主持稿   男:今晚,我们再次欢聚一堂  女:满载丰收的喜悦,用歌舞表达内心最真挚的情感  男:朋友们,古往...
最新公司年会主持词 最新公司年会主持词  主持词已成为各种演出活动和集会中不可或缺的一部分。在当下的社会中,主持词的实用...
毕业晚会主持词结束语 毕业晚会主持词结束语  以下是由应届毕业生网PQ小编为大家整理出来的2016年毕业晚会主持词结束语,...
启动会主持词 启动会主持词  利用在中国拥有几千年文化的诗词能够有效提高主持词的感染力。在现在的社会生活中,主持人...
七十大寿祝寿主持词最新 七十大寿祝寿主持词最新  根据活动对象的不同,需要设置不同的主持词。在当下的中国社会,主持词的实用频...
十一国庆节晚会主持词 十一国庆节晚会主持词  在国庆节需要举办相关的晚会,那么晚会的主持词应该怎么写呢?下面是小编分享给大...
中学秋季开学典礼主持词串词 中学秋季开学典礼主持词串词  暑假如同一部赏心悦目的电影,看完了,请你把美好的记忆珍藏在心中,开学日...
适合父亲节的歌曲 适合父亲节的歌曲适合父亲节的歌曲推荐Eric Clapton的.My Father's Eyes,很...
我们的节日端午主持稿 我们的节日端午主持稿(通用12篇)  在当下社会,很多地方都会使用到主持稿,主持稿具有语言过度自然、...
婚礼主持词开场白   第一篇:  尊敬的各位来宾,  很高兴接受一对新人的委托,主持今天这场爱情的盛典,我是主持人张 ...