C++ 多态 纯干货讲解 复制可调试(1)

编程入门 行业动态 更新时间:2024-10-12 12:33:49

C++ 多态 纯<a href=https://www.elefans.com/category/jswz/34/1767986.html style=干货讲解 复制可调试(1)"/>

C++ 多态 纯干货讲解 复制可调试(1)

💯 博客内容:多态

😀 作  者:陈大大陈

🚀 个人简介:一个正在努力学技术的准C++后端工程师,专注基础和实战分享 ,欢迎私信!

💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

目录

虚函数继承的条件

普通调用

多态调用

例:

易错练习题

override 和 final

纯虚函数 

多态的本质

为什么对象不能实现多态?


 

虚函数继承的条件

1.虚函数重写

2.必须是父类的引用或者指针来调用 

普通调用

调用函数的类型是谁,就调用哪个类型的函数

多态调用

调用指针或者引用指向的对象,指向父类就调用父类,指向子类就调用子类。 

例:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:virtual void BuyTickets(){cout << "全价买票" << endl;}
};
class Student :public Person
{virtual void BuyTickets(){cout << "半价买票" << endl;}
};
void Func(Person& p)
{p.BuyTickets();
}
int main()
{Student st;Func(st);Person p;Func(p);}

 上面的代码要是把引用去了,就是两个全价买票,因为普通调用只看参数类型。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:virtual void BuyTickets(){cout << "全价买票" << endl;}~Person(){cout << "~Person" << endl;}
};
class Student :public Person
{
public:virtual void BuyTickets(){cout << "半价买票" << endl;}~Student(){cout << "~Student" << endl;}
};
void Func(Person& p)
{p.BuyTickets();
}
int main()
{Student st;Func(st);Person p;Func(p);}

一般情况下,析构没有问题。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:virtual void BuyTickets(){cout << "全价买票" << endl;}~Person(){cout << "~Person" << endl;}
};
class Student :public Person
{
public:virtual void BuyTickets(){cout << "半价买票" << endl;}~Student(){cout << "~Student" << endl;}
};
void Func(Person& p)
{p.BuyTickets();
}
int main()
{/*Student st;Func(st);Person p;Func(p);*/Person* ptr = new Person;delete ptr;ptr = new Student;delete ptr;}

 在new的情况下就会有问题,因为析构没有满足多态的条件,所以析构是普通调用。

加上virtual即可。

接下来来看一道易错练习题。

易错练习题

 答案是B,容易选成D,需要注意的是,虚函数继承声明,重写实现。

派生类可以不加virtual,所以B的func接口继承了A。

所以val的值是1。

之后B对象切片后传参调用A的test,A的test调用B的func。

如果调用的语句是,p->func(),就不构成多态,答案会是B->0。

override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

1. final:修饰虚函数,表示该虚函数不能再被重写

2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car{ public:virtual void Drive() final {}};class Benz :public Car{ public:virtual void Drive() {cout << "Benz-舒适" << endl;}};


 

class Car{public:virtual void Drive(){}};class Benz :public Car {public:virtual void Drive() override {cout << "Benz-舒适" << endl;}}; 

纯虚函数 

 在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。


class Car
{ public:virtual void Drive() = 0;
};class Benz :public Car{ public:virtual void Drive(){cout << "Benz-舒适" << endl;} };class BMW :public Car{ public:virtual void Drive(){cout << "BMW-操控" << endl;} };void Test(){Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive(); }
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class base
{
public:virtual void func1(){cout << "func1()" << endl;}virtual void func2(){cout << "func2()" << endl;}void func3(){cout << "func3()" << endl;}
private:int _b = 1;char _ch;
};int main()
{cout << sizeof(base) << endl;base b;return 0;
}

虚函数里会存一个虚函数表指针来保存地址。

vfptr(virtual function pointer);

它是一个函数指针数组。 

父类和派生类的虚表指针不尽相同,重写了哪个虚函数,哪个虚表指针就会被覆盖。 

也就是说:

至此,我们明白了多态的本质

多态的本质

多态为什么能指向父类调父类,指向子类调子类?

虚函数的重写也叫虚函数的覆盖。

编译器首先在对象里找到虚函数表的地址。

查找虚函数表中存储的地址,是父类就调用父类,是子类就切割成父类对象。

是因为虚表指针会去寻找所调用的类的地址。 

为什么对象不能实现多态?

 因为指针和引用可以指向子类对象中切割出来的父类的一部分。

而对象的拷贝不会拷贝虚表指针。

如果拷贝虚表指针的话,父类和子类的虚函数以及析构函数极易混淆,造成报错。

更多推荐

C++ 多态 纯干货讲解 复制可调试(1)

本文发布于:2023-11-16 17:03:18,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1627393.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:干货   多态

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!