首页 > 其他分享 >1-3 多态、模板

1-3 多态、模板

时间:2023-08-08 10:56:40浏览次数:33  
标签:函数 子类 多态 template 父类 模板

1 多态

多态分两类:

  • 静态多态:函数重载和运算符重载,即复用函数名
  • 动态多态:派生类和虚函数来实现运行时多态

区别:

  • 静态多态在编译阶段确定函数地址
  • 动态多态在运行阶段确定函数地址,根据传入的对象不同确定具体的执行函数

动态多态满足条件:

  • 首先要有继承关系
  • 子类要重写父类的虚函数,重写的时候函数名和形参列表以及返回类型都要相同

动态多态的使用:
父类的指针或引用指向子类的对象

class Animal
{
public:
    //虚函数
    virtual void speak(){
        cout<<"动物在说话"<<endl;
    }
};

class Cat:public Animal
{
public:
    void speak(){
        cout<<"猫在说话"<<endl;
    }
};

void test01(Animal &animal){
    animal.speak();
}

int main()
{
    Cat cat;
    test01(cat);//父类引用可以指向子类
    return 0;
}

如果父类中不是虚函数,输出就是动物在说话
变成虚函数后,输出就是猫在说话

父类函数为普通函数时,编译器就会在编译时认定调用对象就是animal,调用的函数地址就确定为animal中的speak;而虚函数会在运行阶段依据传入的参数来确定调用对象,进而确定是执行哪一个派生类的函数代码

2 多态的本质

前面已经知道,非静态成员和类是分开存储的,但是当我们用sizeof查看定义了虚函数的类之后,可以发现虚函数也是和静态成员一样和类存储在一起,它们是属于类的。

当我们在定义一个虚函数之后,类内会维护一个虚函数指针vfptr,而这个虚函数指针指向一个虚函数表vftable,在这个虚函数表中放着我们的虚函数地址。

而多态就是可以根据传入不同的子类对象,来指向对应子类的虚函数表,要注意的是,父类和子类会各自维护自己的虚函数表,但是虚函数指针只有一个

3 多态的应用

设定一个计算器类

class Calculator
{
public:
    int m_Num1;
    int m_Num2;
    int getResult(string opt){
        if(opt == "+"){
            return m_Num1 + m_Num2;
        }else if(opt == "-"){
            return m_Num1 - m_Num2;
        }else if (opt == "*"){
            return m_Num2 * m_Num1;
        }
    }
    
};

void test01(){
    Calculator c;
    c.m_Num1 = 10;
    c.m_Num2 = 10;
    cout<<c.getResult("+");
}

int main()
{
    test01();
    return 0;
}

如果想要添加新的操作,这个时候就只能修改源码,实际中最好减少修改,但是允许扩展,所以接下来用多态实现

class AbstractCalculator//抽象类基类
{
public:
    int m_Num1;
    int m_Num2;

    virtual int getResult(){

    }
};

class AddCalculator:public AbstractCalculator
{
public:
    int getResult(){
        return m_Num1 + m_Num2;
    }
};

class SubCalculator:public AbstractCalculator
{
public:
    int getResult(){
        return m_Num1 - m_Num2;
    }
};

void test02(){
    //加法
    AbstractCalculator *abc = new AddCalculator;//父类指针指向子类对象
    abc->m_Num1 = 10;
    abc->m_Num2 = 5;
    cout<<abc->getResult()<<endl;
    delete abc;
    //减法
    abc = new SubCalculator;
    abc->m_Num2 = 5;
    abc->m_Num1 = 10;
    cout<<abc->getResult()<<endl;
    delete abc;
}

int main()
{
    test02();
    return 0;
}

由此可以看出多态的好处:

  • 我们只需要拓展的去写新功能,基类定义好要做什么,子类进行具体实现,利用多态实现
  • 结构清楚,利于维护

4 纯虚函数和抽象类

在多态中,通常父类中的虚函数不进行具体实现,主要还是子类重写内容

因此完全可以写成纯虚函数

virtual 返回值类型 函数名 (参数列表)= 0;

当类中有纯虚函数时,我们称该类为抽象类。

抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

5 虚析构和纯虚析构

使用多态时。如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构函数

解决方法:将父类中的析构函数改为虚析构或者纯虚析构,用以解决父类指针释放子类对象

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名()= 0;

但是纯虚析构必须要有实现,比如在类外进行类名::~类名(){}的实现,且拥有纯虚析构的类也属于抽象类,无法实例化对象

总结:

  • 子类中有堆区数据时必须实现虚析构或纯虚析构,没有时不用写;
  • 抽象类不能实例化对象

6 模板

1 模板的概念

模板就是建立通用的模具,大大提高复用性,比如PPt中的模板,样式框架已经放好了,把内容放进去就行,不同的内容实现了不同的ppt

C++中泛型编程主要用模板,模板有两种:

  • 函数模板
  • 类模板

2 函数模板

建立一个通用函数,函数返回值类型形参类型可以不具体指定,用一个虚拟的类型表示

语法:

template<typename T>
函数声明或定义
  • template--声明创建模板
  • typename--表明其后面的符号是一种数据类型,可用class替代
  • T--通用数据类型,也可用任意大写字母替换

使用:
比如我们要交换两个数,但是由于数据类型有多个,我们需要写多个函数swapInt、swapDouble....于是我们用模板来减少重复代码

template<typename T>//声明一个模板
void mySwap(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}

//使用函数模板
//1自动类型推到
  mySwap(a,b);
//2.显示指定类型
  mySwap<int>(a, b);

注意事项:

  • 自动类型推导,必须推导出一致的数据类型T才可以使用
  • 模板只有确定了类型T才能使用

比如:

template<typename T>
void func(){
    cout<<1;
}

//使用
func<int>();

3 普通函数和函数模板之间的区别:

  • 普通函数调用时可以发生自动类型转换(隐式转换)
  • 函数模板调用时如果利用自动类型推导,没有自动类型转换
  • 但是用显示调用函数模板时,可以自动类型转换

4 普通函数和函数模板的调用规则

  • 如果函数模板和普通函数都实现了,优先调用普通函数
  • 可以通过空模板参数列表来强制调用函数模板,如myPrint<>(a, b);
  • 函数模板也可以重载
  • 如果函数模板可以产生更优的匹配,优先调用函数模板

5 函数模板的局限性

在使用函数模板的时候,内部会有比较之类的操作,如果遇到类对象作为参数的时候,就会出问题,这个时候需要为类对象另外开辟一条路径,比如:

template<typename T>
bool myCompare<T &a,T &b);

templat<> bool myCompare(Person &p1, Person &p2)

6 类模板

建立一个通用的类,类中成员和数据类型可以不具体指定

template<typename T,...>
类名

使用实例:

template<class NameType, class AgeType>
class Person
{
public:
    NameType m_Name;
    AgeType m_Age;
    Person(NameType name, AgeType age){
        this->m_Name = name;
        this->m_Age = age;
    }
};


int main()
{
    Person<string, int> p1("qqqq",28);

    return 0;
}

7 类模板和函数模板的区别

  1. 类模板没有自动类型推导,要显示使用给出参数类型
  2. 类模板允许默认参数,如:
template<class NameType, class AgeType = int>

8 类模板中成员函数创建时机

普通类中成员函数一开始就可以创建,类模板中成员函数在模板被调用时才去创建,因为类模板的类型一开始无法确定

9 类模板与继承

类模板被继承时要遵循以下规则:

  • 子类继承的是父模板时,子类在声明的时候要指定父类中的T
  • 如果不指定,编译器无法为子类分配内存
  • 如果需要灵活指定出父类中T类型,子类也需要变为类模板

比如:

template<class T>
class Base
{
    T m;
};

//如果不指定int,会报错
class Son:public Base<int>
{
    
};

//如果想灵活指定父类中T的类型,子类也写成可变模板,用T2来指定T
template<class T1, class T2>
class Son2:public Base<T2>
{
    
};

10 成员函数的类外实现

模板头和模板参数列表都要写

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){}

11 类模板分文件编写

类模板中成员函数的创建实在调用阶段,导致分文件编写时链接不到

解决:

  • 方式1:直接包含.cpp源文件
  • 方式2:将声明和实现写到同一个文件中,并改后缀为.hpp,hpp是约定名称,不强制

标签:函数,子类,多态,template,父类,模板
From: https://www.cnblogs.com/dreamer-q/p/17610051.html

相关文章

  • 背包问题的一些模板
    01背包问题:无优化for(inti=1;i<=n;i++){for(intc=0;c<=m;c++){f[i][c]=f[i-1][c];if(c>=w[i])f[i][c]=max(f[i][c],f[i-1][c-w[i]]+v[i]);}}一维数组优化:for(inti=1;i<=n;i++){for(intc=m;c>=0;c--){......
  • 6模板语法
    创建一个vue3项目npminitvue@latest启动cdvue-demonpminstallnpmrundev修改App.vue这时候打开时空白再修改<template>{{msg}}</template><script>exportdefault{data(){return{msg:"神奇的语法"}}}</script>......
  • django模板使用的总结(2)
    项目模板使用分析模板总结1,主要讲了一些原理和使用方法。现在开始在项目上进行实操分析。我们的博客主要有:网站首页、文章分类列表页、搜索列表页、标签列表页、文章内容展示页、单页面(联系我们)。其中,文章分类列表页、搜索列表页、标签列表页这三个页面展示结构都一样我们只需要......
  • Django 模板table 自增序号列
    第一种方法:<styletype="text/css">table{counter-reset:tableCount;}.counterCell:before{content:counter(tableCount);counter-increment:tableCount;}</style>标签中使用<table><tr><......
  • django模板使用的总结
    一、静态资源的引入方式1.在项目根目录下创建static文件夹。2.settings.py中配置环境变量,方便程序可以识别此路径。要在STATIC_URL='/static/'下边添加下面代码STATICFILES_DIRS=[os.path.join(BASE_DIR,'static'),]或STATICFILES_DIRS=os.path.join(BAS......
  • springboot智能3D导诊系统源码,基于规则模板的开发原理
    互联网智慧3D导诊系统源码通过智能导诊,进行自助问询及挂号服务,减轻导诊台护士压力,挂号更加方便快捷。技术架构:springboot+redis+mybatisplus+mysql+RocketMQ  智慧导诊系统开发原理导诊系统从原理上大致可分为基于规则模板和基于数据模型两类。1、基于规则推理的方法通过人工建......
  • 代码填充模板-膜拜神器
    { //Placeyoursnippetsforcpphere.Eachsnippetisdefinedunderasnippetnameandhasaprefix,bodyand //description.Theprefixiswhatisusedtotriggerthesnippetandthebodywillbeexpandedandinserted.Possiblevariablesare: //$1,......
  • 【模板】线段树
    引入题目描述给定\(n\)个数\(a[1],a[2],a[3]...a[n]\),现在又下面两种操作:1.询问区间\([x,y]\)的和,并输出。2.将下标为\(x\)的数增加\(val\)。一共\(x\)此操作\(1\len,m\le100000\),保证在\(int\)范围内。方法一:暴力枚举定义数组\(a\)储存\(n\)个元素。求区间和的时间复......
  • [Unity]URP HLSL Shader自用模板
    Shader"URP/falushan"{Properties//着色器的输入{_BaseMap("Texture",2D)="white"{}}SubShader{Tags{"RenderType"="Opaque""RenderPipeL......
  • 模板方法模式
    **模板方法模式:**在一个方法中定义一个算法的骨架,现将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。在模板模式(TemplatePattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象......