【C++】多态(早期绑定、后期绑定)、抽象类(纯虚函数)、虚析构函数

news/2024/7/5 10:57:49

我们都知道面向对象编程的三大特征是封装、继承、多态,今天我们就来说一下其中之一的多态。

概念:

多态:

多态字面意思就是多种形态,C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。(取自菜鸟教程)

多态分类:

多态分为编译时多态(早期绑定)和运行时多态(后期绑定)

编译时多态(早期绑定=静态联编)

概念:

        通过函数重载和运算符重载来实现。

特点:

        在编译时我们就已经知道调用的是哪些个函数

看下面的代码:

int Max(int a, int b) { return a > b ? a : b }
char Max(char a, char b) { return a > b ? a : b }
double Max(double a, double b) { return a > b ? a : b }int main()
{int x = Max(12, 23);char ch = Max('a', 'b');double de = Max(12.23, 34.45);return 0;
}

上述代码我们通过重载函数Max来实现早期绑定,也就是在我们编译的时候通过函数重载的机制就已经知道调用的是哪个函数,比如Max(12,23)参数为int整型类型的参数,所以我们就知道调用的是第一个int Max()这个函数。

运行时多态(动态绑定=动态联编)

概念:

        运行时的多态性是指在程序执行前,无法根据函数名和参数来确定该调用哪一个函数,必须在程序执行过程中,根据执行的具体情况来动态地确定。它是通过类继承关系public和虚函数来实现的。

特点:
        同一个方法在不同的对象身上体现出来不同的代码形式,但是我们有统一的接口,例如下面代码中的fun函数这个方法,在基类中和派生类中体现出来的方法不一样!!!

        就是说运行时多态是通过虚函数来实现的。虚函数在我上一篇说过:【C++】虚函数_神厨小福贵!的博客-CSDN博客虚函数是构成C++多态的重要一步,今天来说一下虚函数!虚函数:在基类(或父类)中,使用virtual关键字对函数进行声明为并在一个或多个派生类(子类)中被重新定义的成员函数,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。它的用法是这样的:virtual + 函数返回类型 + 函数名 +(参数表) {函数体}首先我们要知道为什么要有虚函数这个东西?我来看下下面这两段代码的结果再来说结果:class A{public:void fun(){couthttps://blog.csdn.net/qq_45829112/article/details/123460960?spm=1001.2014.3001.5502

下面我那代码来实现一下动态绑定:

class A
{
public:virtual void fun(){}
};class B :public A
{
public:virtual void fun(){cout << "class B::fun()" << endl;}
};class C :public A
{
public:virtual void fun(){cout << "class C::fun()" << endl;}
};void pun(A& a)
{a.fun();
}int main()
{B b;C c;pun(b);pun(c);return 0;
}

类B、C都公有继承类A,然后创建bc对象,分别调用函数pun,当传入参数为class B参数时 调用B中的妇女函数,传参为class C时,调用类C中的fun函数!、

上述代码解析如下图所示:

运行结果:

这就是简单的运行时的多态,相同的方法在不同的 不同对象下进行调用时,产生的结果也不尽相同,这也就是简单的运行时的多态!!!

抽象类(纯虚函数)

概念:

        含有纯虚函数的类称为抽象类。纯虚函数没有实现部分,不能产生对象。

        简单来说就是抽象类(有纯虚函数的类)不能定义对象!!!

        virtual  类型  函数名(参数列表)= 0  如下所示:

virtual void fun() { } = 0;

下面来说下纯虚函数的特点:

        抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类没有重新定义纯虚函数,而派生类只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体类了。
 

        可能会有些抽象,举个简单例子就是说,你说你要去混社会去了,但是有社会这个实体这个对象嘛,显然是没有的,我们只能在社会上做某些事情,而没有社会这个实体,大致就这么个意思!!!

那么纯虚函数以及这个抽象类有什么作用呢???

class A
{
public:virtual void fun() {} = 0; //纯虚函数
};class B :public A
{
public:virtual void fun()   //必须要进行重写,否则class B也是一个抽象类,不可以定义对象{cout << "class B::fun()" << endl;}
};class C :public A
{
public:virtual void fun()  //必须要进行重写,否则class C也是一个抽象类,不可以定义对象{cout << "class C::fun()" << endl;}
};

        抽象类一般作为接口来使用,真正的实现都是在其派生类中进行实现,如上述代码中所示,在基类A中的fun啥也不干,仅仅搞个纯虚函数,真正的实现都是在其派生类class B和class C中来进行实现的。

析构函数为什么声明为虚函数???

class A
{
public:A(int x = 10){cout << "A构造函数运行" << endl;}virtual void fun(){}~A(){cout << "~A运行" << endl;}
};class B :public A
{
public:B(int x = 10){cout << "B构造函数运行" << endl;}~B(){cout << "~B运行" << endl;}virtual void fun(){cout << "class B::fun()" << endl;}
};int main()
{A* op = new B(10);op->fun();delete op;op = nullptr;return 0;
}

观察上述代码,父类指针指向派生类对象,完成执行fun函数之后,我们对op指针所指向资源进行释放以及置空,运行结果:

 为啥上面我们构造了两次,最后释放却只释放了一次呢?

        原因是我们op是基类指针,指向派生类的对象,构造class B的无名对象的时候,先构造A的隐藏父对象,再构造B对象,但是我们释放的时候因为是基类指针,所以只能找到基类的析构函数,所以我们派生类的对象就没有进行释放,可能会造成内存泄漏!!!

怎么解决这个问题呢?父类析构函数加虚声明virtual:

class A
{
public:A(int x = 10){cout << "A构造函数运行" << endl;}virtual void fun(){}virtual ~A()   //构造函数加virtual声明{cout << "~A运行" << endl;}
};class B :public A
{
public:B(int x = 10){cout << "B构造函数运行" << endl;}~B(){cout << "~B运行" << endl;}virtual void fun(){cout << "class B::fun()" << endl;}
};int main()
{A* op = new B(10);op->fun();delete op;op = nullptr;return 0;
}

至于派生类的析构函数,我们可以加也可以不加,因为如果我们派生类还有派生类的话,就得加virtual声明,下面我们来看运行结果:

我们在基类中将析构函数声明为virtual虚函数,派生类相当于继承了虚函数这个特性,所以析构的时候,就可以通过虚表指针来找到派生类中的虚函数来对派生类对象进行析构!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

【C++】多线程thread

进程和线程这部分呢我之前在我Linux中写过这些东西&#xff0c;和C中线程的概念差不多&#xff0c;大家可以去看一下&#xff1a; Linux多线程_神厨小福贵&#xff01;的博客-CSDN博客进程和线程的区别有哪些呢&#xff1f;进程是资源分配的最小单位&#xff0c;线程是CPU调度…

【C++】多线程互斥锁、条件变量

我们了解互斥量和条件变量之前&#xff0c;我们先来看一下为什么要有互斥量和条件变量这两个东西&#xff0c;了解为什么有这两东西之后&#xff0c;理解起来后面的东西就简单很多了&#xff01;&#xff01;&#xff01; 先来看下面这段简单的代码&#xff1a; int g_num 0;…

【C++】二叉树的先序、中序、后序遍历序列

二叉树常用到的遍历有这三种 先序遍历&#xff1a;先遍历根节点&#xff0c;然后再分别遍历左节点和右节点。(根左右) 中序遍历&#xff1a;先遍历左节点&#xff0c;然后再遍历根节点&#xff0c;最后遍历右节点。(左根右) 后序遍历&#xff1a;先遍历左节点&#xff0c;然…

软件工程需求设计说明书

Java即时通聊天程序 设计需求说明书 专业班级&#xff1a; 计本班1202班 项目组成员&#xff1a; 杨宗坤 刘瑞 满亚洲 指导教师&#xff1a; 张利峰 开始日期&#xff1a; 完成日期&#xff1a; 编写目的&#xff1a; 本说明书是在充分理解系统需求分析…

【C++】菱形继承

我们先来看下菱形继承的基本视图以及基本的代码结构 下面来看下简单的代码以及数据结构&#xff1a; class Person { public:int a_p; };class Studen :public Person { public:int a_st; };class Stuff :public Person { public:int a_sf; };class st_sf :public Stuff, publ…

ie旋转滤镜Matrix

旋转一个元素算是一个比较常见的需求了吧&#xff0c;在支持CSS3的浏览器中可以使用transform很容易地实现&#xff0c;这里有介绍&#xff1a;http://www.css88.com/archives/2168&#xff0c;这里有演示http://www.css88.com/tool/css3Preview/Transform.html&#xff0c;就不…

【C++】四种类型的转换

C四种类型的转换 包括这四种&#xff1a;const_cast , static_cast , dynamic_cast , reinterpret_cast 先来说下C语言中的类型转换&#xff0c;非常的暴力&#xff0c;就是耍流氓&#xff1a; float a 12.23; int b (int)a; 下面我写的都是最基础的&#xff0c;简单的&am…

【C++】满二叉树、完全二叉树等概念解释

二叉树中的判断有以下几种&#xff1a; 是否完全二叉树、是否满二叉树、是否为BST树、是否为平衡二叉树、是否为对称二叉树、完美二叉树 满二叉树&#xff1a; 除最后一层无任何子节点外&#xff0c;每一层上的所有结点都有两个子结点的二叉树。 上述所示图除最外一层节点之外…