C++继承和多态特性——多态特性与虚函数

一、多态和虚函数

1、通过案例学多态

(1)案例:父类Animal,2个子类Dog和Cat,实现speak方法

(2)用父类指针指向各对象,调用各方法看效果,记下来

#include  <iostream>

using namespace std;

//基类
class Animal
{
public:
    void speak(void);

};


void Animal::speak(void)
{

    cout << "Speaking!" << endl;

}  

//子类
class Dog:public Animal
{
public:
    void speak(void);


};

void Dog::speak(void)
{
    cout << "wang wang wang!" << endl;


}

class Cat:public Animal
{
public:
    void speak(void); 

};

void Cat::speak(void)
{
    cout << "miao miao miao!" << endl;

}

int main(int argc, char *argv[])
{
    Animal a;
    Dog d;
    Cat c;
    
    a.speak();
    d.speak();
    c.speak();

    Animal *p; 
    p = &a;
    p->speak();

    p = &d;
    p->speak();   
    
    p = &c;
    p->speak();

    return 0;
}

在这里插入图片描述
(3)将父类speak方法声明为virtual,再用父类指针调用各方法看效果,记下来

//基类
class Animal
{
public:
    virtual void speak(void);

};

在这里插入图片描述
(4)对比差异,理解什么叫多态

2、什么是多态

(1)polymorphism,多态,面向对象的三大特征之一。

(2)从宏观讲,多态就是要实现一套逻辑多种具体适配的执行结果。猫就应该是猫的叫声,狗就应该是狗的叫声.

(3)从微观讲,多态就是要一套代码在运行时根据实际对象的不同来动态绑定/跳转执行相匹配的具体函数.

(4)函数声明前加virtual的即是虚函数

(5)虚函数是C++实现多态特性的基础,从语法上讲多态特性的基类方法必须是虚函数

3、多态中的override

(1)基类中方法声明为virtual,派生类中重新实现同名方法以实现多态,这就叫override(中文为覆盖,或重写)

(2)注意区分override和redefining,微观上最大区别就是是否有virtual,宏观上最大区别就是是否表现为多态

4、多态一定要通过面向对象和override来实现吗?

(1)宏观上的多态是一种编程效果,微观上的多态是一种C++支持的编程技术,微观是为了去实现宏观

(2)不用C++的virtual和override,也可以实现宏观上的多态,C中我们就经常这么干。

(3)C中实现多态的案例:

#include <stdio.h>

enum Animal
{
    Cat,
    Dog
};

void Dog_speak(void)
{
    printf("wang wang wang!\n");
}

void Cat_speak(void)
{
    printf("miao miao miao!\n");
}


void speak(int type)
{
    
    if (type == Dog)
    {
        Dog_speak();
    }
    if (type == Cat)
    {
        Cat_speak();
    }

}

int main(int argc, char *argv[])
{
    int tmp;

    scanf("%d", &tmp);
    speak(tmp);

    return 0;
}

(4)C++源生支持多态,实现起来更容易,后续修改和维护更容易,架构复杂后优势更大。

5、对比重载、重定义、重写三个概念

(1)overload,重载  同一个类里面的多个方法,函数名相同但参数列表不同

(2)redifining,重定义,隐藏  继承中子类再次实现父类中同名方法然后把父类方法隐藏掉

(3)override,覆盖,重写  继承中子类去实现父类中同名virtual方法然后实现多态特性

二、纯虚函数与抽象类

1、纯虚函数

(1)纯虚函数就是基类中只有原型没有实体的一种虚函数

(2)纯虚函数形式:virtual 函数原型=0;

纯虚函数在实现多态时与虚函数一样,可当作虚函数使用。

(3)代码实践:在基类Animal中使用纯虚函数

#include <iostream>

using namespace std;

class Animal
{
public:
    virtual void speak(void)=0; 

};

class Cat:public Animal
{
public:
    void speak(void);
};

void Cat::speak(void)
{
    cout << "miao miao miao!" << endl;
}

int main(int argc, char *argv[])
{
    Animal *p;
    Cat c;

    p = &c;
    p->speak();

    return 0;
}

(4)纯虚函数为什么没有实体?
  因为语义上不需要

(5)纯虚函数是否占用内存?
  不会,因为纯虚函数所在的类根本无法实例化对象!

2、抽象类(abstract type)

(1)带有纯虚函数的类成为抽象类。抽象类只能作为基类来派生新类,不可实例化对象(即抽象类不可用来定义对象)
在这里插入图片描述
(2)派生类 必须实现 基类的纯虚函数 后 才能用于实例化对象。
在这里插入图片描述
(3)抽象类的作用:将有关的数据和行为组织在一个继承层次结构中,保证派生类必须具有所要求的方法。对应暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。这种机制可以让语法和语义保持一致。

(4)抽象类的子类必须实现基类中的纯虚函数,这样子类才能创建对象否则子类就还是个抽象类

3、接口(interface)

(1)接口是一种特殊的类,用来定义一套访问接口,也就是定义一套规约

(2)接口类中不应该定义任何成员变量

(3)接口类中所有成员函数都是公有且都是纯虚函数

(4)有些高级语言中直接提供关键字interface定义接口,接口其实就是个纯粹的抽象基类

三、虚析构函数

1、什么是虚析构函数

(1)析构函数前加virtual,则析构函数变为虚析构函数

(2)规则:基类有1个或多个虚函数时(注意不要求是纯虚函数),则其析构函数应该声明为virtual

2、为什么需要虚析构函数

(1)代码演示:父子类各自添加析构函数,用2种分配和回收对象的方式分别实验,观察析构函数被调用的规律

//测试代码1
#include <iostream>
using namespace std;
class Animal
{
public:
    virtual void speak(void)=0; 
    virtual ~Animal();
    //~Animal();//这个和上面的执行结果相同
};

Animal::~Animal()
{
    cout << "~Animal()" << endl;
}

class Cat:public Animal
{
public:
    void speak(void);
    ~Cat();
};

Cat::~Cat()
{
    cout << "~cat()" << endl;
}

void Cat::speak(void)
{
    cout << "miao miao miao!" << endl;
}

int main(int argc, char *argv[])
{
    Animal *p;
    Cat c;

    p = &c;
    p->speak();

    return 0;
}

在这里插入图片描述
上面这种情况实际析构时只执行了子类Cat的析构函数,并没有执行父类Animal的析构函数,是子类的析构函数调用了父类的析构函数.

//测试代码2
#include <iostream>

using namespace std;

class Animal
{
public:
    virtual void speak(void)=0; 
    virtual ~Animal();
    //~Animal();//这个和上面的执行结果不相同
};

Animal::~Animal()
{
    cout << "~Animal()" << endl;
}

class Cat:public Animal
{
public:
    void speak(void);
    ~Cat();
};

Cat::~Cat()
{
    cout << "~cat()" << endl;
}

void Cat::speak(void)
{
    cout << "miao miao miao!" << endl;
}

int main(int argc, char *argv[])
{
    Animal *p = new Cat();//对象是Cat类对象,分配在堆上
    p->speak();
    delete p;

    return 0;
}

在这里插入图片描述
上面这种情况实际析构时只执行了父类Animal的析构函数,并没有执行子类Cat的析构函数。

(2)结论:虚析构函数在各种情况下总能调用正确的(和对象真正匹配的)析构函数。

3、分析和总结

(1)其实虚函数的virtual的价值,就是让成员函数 在运行时 动态解析 和 绑定 具体执行的函数,这是RTTI机制(动态运行时类型识别机制)的一部分

(2)析构函数也是成员函数,加virtual的效果和普通成员函数加virtual没什么本质差异

(3)加virtual是有开销的,运行时动态绑定不如编译时静态绑定效率高资源消耗优,但是可以多态。

四、using重新定义继承时访问权限

1、using关键字 在非public继承时的 权限重开作用

(1)父类的public方法在private/protected继承时,到了子类就成了private/protected而不是public了,无法用子类对象来调用了

(2)解决方法1:是改为public继承,有用但是有时候不得不protected或者private继承时就没办法了

(3)解决方法2:是在子类中再实现一个public的方法,内部调用父类继承而来的那个方法,能用但是有点麻烦而且有额外开销

(4)解决方法3:是在子类中使用using关键字将该方法声明为public访问权限,本质上类似于权限打洞

(5)用法:在子类public声明中使用 using Base::func; 即可,不带返回值类型不带参数列表。

(6)注意:using只用于private/protected继承中的权限损失找回,如果方法在父类中本来就是private的子类中没法using后访问。

#include <iostream>

using namespace std;

class father
{
public:
    void work(void);
    void live(void);
};

void father::work(void)
{
    cout << "I am working." << endl;
}

void father::live(void)
{
    cout << "I am living." << endl;
}

class son:private father
{
public:
    void way(void);
    using father::live;
} ;

void son::way(void)
{
    this->work();
}

int main(int argc, char *argv[])
{
    son s;

    s.way();
    s.live();
    
    return 0;
}

权限 本身是 编译器在编译时的一种设定。

3、C++继承和多态特性总结

(1)本部分主要讲了C++的2个特性:继承和多态,其中继承和权限管控、继承中的构造和析构、隐藏和覆盖、虚函数和多态、纯虚函数和抽象类、接口、静态和动态绑定等是本课程的重点。

(2)面向对象三大特征中:封装是基础、继承是关键、多态是延伸。

(3)本部分内容很重要,属于C++知识体系中关键基础零件,对于以后理解复杂代码和架构意义重大,必须彻底理解和熟悉。

(4)要求要见过、认识、会写、理解原理、熟练运用。

注:本文章参考了《朱老师物联网大讲堂笔记》,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值