C++入门(纯)虚函数和多态抽象类接口
Posted 正在起飞的蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++入门(纯)虚函数和多态抽象类接口相关的知识,希望对你有一定的参考价值。
1、虚函数和多态
(1)虚函数定义:在类中用virtual修饰的成员函数;
(2)纯虚函数:在基类中,虚函数可以有函数实体,也可以没有函数实体,如果没有函数实体则是纯虚函数;含有纯虚函数的类是不能定义对象的;
(3)多态:用基类的指针或者引用指向派生类,访问派生类中同名覆盖的成员函数;
2、虚函数的示例代码
#include <iostream>
using namespace std;
//用来描述动物的基类
class Animal
public:
virtual void speak(void); // 虚函数
//virtual void speak(void) = 0; // 纯虚函数
//void speak(void); //普通函数
;
//用动物基类来构建猫这个类
class Cat : public Animal
public:
void speak(void);
;
//用动物基类来构建狗这个类
class Dog : public Animal
public:
void speak(void);
;
//虚函数的实体
void Animal::speak(void)
cout << "Animal speak" << endl;
void Cat::speak(void)
cout << "miao miao miao" << endl;
void Dog::speak(void)
cout << "wang wang wang" << endl;
int main(void)
Animal *p;
Cat d;
Dog c;
//测试用基类的指针去指向派生类对象时,调用的是基类还是派生类的speak方法
p = &c;
p->speak();
p = &d;
p->speak();
return 0;
3、代码执行结果分析
//基类speak成员定义为普通函数
[root#]$ ./app
Animal speak
Animal speak
//基类speak成员定义为虚函数
[root#]$ ./app
wang wang wang
miao miao miao
(1)基类Animal的speak定义为普通函数:用基类的指针去指向派生类对象时,调用的是基类的speak方法;
(2)基类Animal的speak定义为虚函数:用基类的指针去指向派生类对象时,调用的是派生类的speak方法;
总结:当使用类的指针调用成员函数时,普通函数由指针类型决定,而虚函数由指针指向的对象的实际类型决定;
4、C语言实现上面虚函数的效果(多态)
4.1、示例代码
#include <stdio.h>
typedef enum
ANIAMAL_CAT,
ANIAMAL_Dog
ANIAMAL_TYPE;
typedef struct
ANIAMAL_TYPE type; //动物的种类
int (*speak)(void); //动物叫的方法
Animal;
//猫的叫声
int cat_speak(void)
printf("miao miao miao\\n");
return 0;
//狗的叫声
int dog_speak(void)
printf("wang wang wang\\n");
return 0;
int main()
Animal cat;
Animal dog;
cat.type = ANIAMAL_CAT;
cat.speak = cat_speak;
dog.type = ANIAMAL_CAT;
dog.speak = dog_speak;
cat.speak();
dog.speak();
return 0;
4.2、代码结构分析
[root#]$ ./a.out
miao miao miao
wang wang wang
(1)用Animal结构体来抽象的表示动物,定义了动物的一些共有属性,然后再用Animal结构体来表示猫和狗;
(2)在Animal结构体中定义了speak函数指针,在定义的cat和dog中speak体现出不同的效果,也就是C++中的多态;
总结:C++中的虚函数,和C语言中的函数指针功能类似,但是用C++的虚函数去实现多态要更简单;
5、纯虚函数和抽象类
(1)纯虚函数:基类中虚函数只有声明没有定义函数实体;
(2)纯虚函数的声明:virtual 函数原型=0;
(3)抽象类:带有纯虚函数的类成为抽象类;(注意并不要求全部类的全部函数都要是纯虚函数)
(4)抽象类只能作为基类来构建派生类,不可实例化对象;毕竟纯虚函数没有函数实体,就像C语言里声明了函数指针但是没有指向具体的函数,如果实例化成对象,那声明的函数指针就是野指针;
(5)抽象类的作用:将一类事物的共性抽离出来组织成一个基类,用来构成派生类;可以结合上面动物(基类)、猫(派生类)、狗(派生类)的例子去理解;
(6)继承抽象类的子类必须实现基类的纯虚函数,才能去示例化对象;
6、接口
(1)接口是特殊的抽象类,类中没有成员变量,全是纯虚函数;
(2)接口就是用来定义一套接口,就好像通信协议一样,只关心函数的返回值、传参,并不关心函数的具体实现,纯虚函数都有派生类去实现;
(3)接口就相当于C语言中定义了一个成员全是函数指针的结构体;
7、虚析构函数
7.1、虚析构函数定义
(1)当基类含有1个或多个虚函数时,其析构函数需要声明为virtual虚函数;
(2)将析构函数定义为虚函数,可以保证派生类析构时能调用正确的析构函数;
7.2、示例代码
#include <iostream>
using namespace std;
// 父类 animal,抽象基类 abstract base type
class Animal
public:
virtual void speak(void) = 0; // 纯虚方法
//virtual ~Animal(); //虚析构函数
~Animal(); //普通析构函数
;
// 子类
class Dog : public Animal
public:
void speak(void);
~Dog();
;
Animal::~Animal()
cout << "~Animal" << endl;
Dog::~Dog()
cout << "~Dog" << endl;
void Dog::speak(void)
cout << "wang wang wang" << endl;
int main(void)
#if 0
Animal *p = new Dog(); // 对象是Dog类对象,但是分配在堆上面, 调用的是派生类的析构函数——错误
p->speak();
delete p;
#else
Dog d; // 对象是Dog类对象,分配在栈上, 调用的是派生类的析构函数————正确
Animal *p = &d;
p->speak();
#endif
return 0;
7.3、代码执行结果分析
//普通析构函数:对象分配在堆上,执行的基类的析构函数(错误)
[root#]$ ./app
wang wang wang
~Animal
//普通析构函数:对象分配在栈上,执行的派生类的析构函数(正确)
[root#]$ ./app
wang wang wang
~Dog
~Animal
(1)普通析构函数:对象分配在堆上时,会执行基类的析构函数(错误);
(2)徐析构函数:无论对象分配在栈上还是堆上,都能正确调用派生类的析构函数;
补充:基类和派生类的析构函数关系参考博客:https://blog.csdn.net/weixin_42031299/article/details/127342145;
8、构造函数不能是虚函数
(1)宏观:虚函数的作用在于通过父类的指针或者引用来调用派生类的时候能够正确调用派生类的重写的成员函数;然而构造函数在构建对象的时候就已经自动调用,所以根本不存在上面说的通过父类指针或者引用来调用派生类的构造函数;
(2)微观:虚函数在实现原理上是依赖vtable虚函数表,而虚函数表的构建是依赖构造函数的初始化,所以构造函数是虚函数实现的前提,不能把构造函数实现成虚函数;
总结:宏观上不需要将构造函数实现成虚函数,微观上不支持将构造函数实现成虚函数;
9、虚函数的总结
(1)虚函数的作用是实现面向对象的多态效果,让成员函数在运行时动态解析和绑定具体的执行函数;
(2)将成员函数定义为virtual虚函数是有额外开销的,因为要在运行时动态绑定;如果是普通函数,在编译时就可以确定并绑定完成;
推荐
我会在C++专栏持续根据更新C++相关的知识点,这里也给大家推荐一款学习C++的神器,我也是在用这一款神器在学习C++。
链接:学习神器跳转
如果你是想入门C++这门语言或者是找C++岗位的工作,都推荐你试试这个网站,里面有针对C++知识点的选择题、编程题,更有C++岗位的面试题,还可以在里面交流行业信息
以上是关于C++入门(纯)虚函数和多态抽象类接口的主要内容,如果未能解决你的问题,请参考以下文章