C++基础之​多态分类及其简单介绍

前言

多态实际上是为了开发的便利设计的,可以理解为一个函数根据传入参数的不同有不同的实现->一个函数有多种状态,及多态。简单来说就是​一个接口多种实现。

  • 一个类内 函数同名参数不同->函数重载
    • 命名倾轧:对函数名结合参数重命名 C中不支持,C++支持
  • 父子类:一般来说 函数重写就是虚函数重写 为的是实现多态调用
    • 1.函数同名参数相同
      • 虚函数重写->多态
        • 基类有virtual且无static->同名覆盖 即函数重写/覆盖
        • 可通过::访问被隐藏的父类同名成员
        • 有virtual则为虚函数
        • 纯虚函数就是虚函数=0
          • 纯虚函数并不实现具体函数,此时子类的同名成员函数强制实现
      • 非虚函数重写->重定义的一种形式
        • 无virtual->函数重定义/隐藏
    • 2.函数同名参数不同->函数重定义/隐藏

一、清晰几个概念

1.继承

  • 继承是面向对象中代码复用的重要手段
    • 语法形式如下
class 派生类名:基类名表
{
	数据成员和成员函数声明
};
//基类名表构成
访问控制 基类名1,,访问控制 基类名2,...,基类名n
//访问控制表示派生类对基类的继承方式,使用关键字:
public 		公有继承0父类成员在子类中不变
private 	私有继承-父类成员在子类中变为private成员
protected 	保护继承-父类的public->protected,protected和private不变
  • 示例:
//描述继承关系 表示C的类继承基类A和B
class C: public A,B

2.基类与派生类

  • 被继承的类叫基类(或父类),继承后的类叫派生类(或子类)
  • 上面的示例中,C为子类/派生类,A和B为父类/基类
  • 派生类不能直接使用基类的私有成员
  • 基类是特殊的父类

3.同名覆盖

  • 父子间的同名成员冲突->同名覆盖
  • 子类可以使用作用域分辨符::访问父类中的同名成员
  • 子类的函数将隐藏父类的同名函数
  • 函数重写是同名覆盖的一种特殊情况

二、多态的分类

C++多态的简单分类

1.静多态

在编译阶段确定,通过函数重载实现

1.1函数重载

​函数同名但不同参,同一作用域中(即同一个类中)

  • 返回值类型可以不同
void func(int a){cout <<"a = "<<a<<endl;}
void func(double a){cout <<"a = "<<a<<endl;}
void func(int a,char b){cout <<"a = "<<a<<"b = "<<b<<endl;}
  • 原理:编译过程把同名函数进行命名倾轧,例如
//定义:
void func(int a ){}
// 编译过程:函数名->函数名_参数
void func_i(int a){}
  • 故而在C中是不支持函数重载的,在C语言中,C编译器仅对函数名简单的重命名
  • 编译过程就进行了命名倾轧,即对函数名结合参数重命名
1.2一些概念:命名倾轧、重载二义性
1.2.1命名倾轧

通过上面你可以了解到,命名倾轧即编译过程对函数名重命名。
C++的定义和使用需要对应统不倾轧和不倾轧,默认倾轧的

  • 不能定义倾轧使用不倾轧,这就引申另外一个问题,兼容C时要设置不倾轧extern "C"
原因:​C库是在链接时加入(此时是不倾轧),而在C头文件中声明时默认使用倾轧,故是要将头文件的声明设置为不倾轧,以此与库中的相互对应。很多C的标准头文件都有extern "C"
1.2.2重载二义性-理解就行了

一句话就是编译器不知道调用哪个重载函数,即候选函数不唯一->二义性,不过编译器会提示的,不用担心~

来一些例子,出现了二义性

  • 隐式转换
    ​如double类型的可以隐式转换为float或者in
void func(int a){ cout << "a = " << a << endl; }
void func(float a){ cout << "a = " << a << endl; }
int main(void)​​ { 
	//double向属int和float都可以转换,编译器不知道该调用那个func(-4.4);
}
  • 默认参数
class Box {
public:
	void func(int a, int b = 0) {}
	void func(int a) {}
};
int main()
{
	Box box;
	box.func(1);//存在默认参数,编译器不知道找哪个
	return 0;
}

2.动多态

在运行阶段确定,通过函数重写实现

2.1函数重写

​函数同名同参,不同作用域中(即基类与派生类中)

  • 在2个类之间,其中基类有virtual关键字,派生类中可有可无,不能有static
  • 父类和子类发生重新的函数完全相同:返回类型+函数名+参数列表
  • 会在运行时再去确定对象的类型以及正确的调用函数
2.2一些概念:虚函数、纯虚函数、重定义/隐藏
2.1.1.虚函数
  • 成员函数加上关键字virtual就是虚函数
  • ​存在关键字virtual时,编译器使用迟绑定
    • 没有virtual关键字时,C++编译器在编译时就确定了哪个函数被调用,这叫做早期绑定
    • 存在virtual关键字是,编译器使用迟绑定,会在运行时再去确定对象的类型以及正确的调用函数
  • 释义:在某基类中声明为 virtual并在一个或多个派生类中被重新定义的成员函数
  • 用法格式为:virtual函数返回类型 函数名(参数表) {函数体};
  • 调用:通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数
    • 定义与声明示例如下
class A
{
    public:
        virtual void print(){cout<<"This is A"<<endl;}
};
class B : public A
{
    public:
    void print(){cout<<"This is B"<<endl;}
};
  • 调用示例
#include<iostream>
using namespace std;
int main()
{
    //调用指针函数
	A a = new A();
	B b =new B();
	b->print();//打印This is B
	delete a;
	a= NULL;
	delete b;
	b = NULL;
	return 0;
}
2.1.2.纯虚函数
  • 纯虚函数就是虚函数=0
  • 是一种特殊的虚函数
  • 声明为纯虚函数则在基类中不能对虚函数给出有意义的实现,而把它的实现留给该基类的派生类去做
  • 派生类中不需要实现该成员函数时 不要将基类的同名函数定义为纯虚函数,因为纯虚函数并不实现…
  • 纯虚函数在的基类称为抽象类。
class A
{
    public://纯虚函数并不实现具体函数
        virtual void print() = 0;
};
class B : public A
{
    public://此时派生类的同名成员函数强制实现
    void print(){cout<<"This is B"<<endl;}
};
2.1.3.重定义/隐藏
  • 只能出现在继承结构中
  • 非虚函数中同名函数会进行重定义即同名覆盖

三、总结

  • 函数重载
    • 同一作用域中,同名函数参数不同
  • 函数重写/覆盖
    • 不同作用域中,同名函数参数相同+virtual
  • 函数重定义/隐藏
    • 不同作用域中,同名函数参数相同+没有virtual

实际上平时说的多态都是动多态,用虚函数实现,即用父类的指针引用子类的同名函数(函数重写)以实现统一接口,多次调用。
纯虚函数在虚函数的基础上,不实现父类的同名成员函数,仅实现子类的函数,主要是强制统一,这个根据实际判断需不需要纯虚函数

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值