C++基础之关键字——virtual详解

virtual修饰

virtual可以修饰普通成员函数和析构函数;

不可以修饰构造函数,友元函数,static静态函数;

virtual具有继承性:父类中定义为virtual的函数在子类中重写的函数也自动成为虚函数。

修饰普通成员函数

被修饰的函数称为虚函数,是c++中多态的一种。当父类的指针指向或者引用其子类的对象时,当使用指针或者引用调用函数的时候会根据具体的对象类型调用对应对象的函数。

调用对应对象的函数,需要两个条件:一是父类的函数是虚函数,二是子类重写父类的函数。

多态分为:编译时多态-通过重载实现;

运行时多态-通过虚函数实现;

复制代码
#include <iostream>

class father {
public:
	void func1() {std::cout << "this is father func1" << std::endl;}
	virtual void func2() {std::cout << "this is father func2" << std::endl;
}

class son:public father {
public:
	void func1() {std::cout << "this is son func1" << std::endl;}
	void func2() {std::cout << "this is son func2" << std::endl;
}

int main() {
	father *f1 = new son();
	f1.func1();
	f1.func2();
	return 0;
}
// 结果:
this is father func1
this is son func2

使用virtual修饰的函数会根据实际对象的类型来调用,没有使用virtual修饰的根据指针的类型来调用。虚函数最关键的特点是"动态联编",它可以在运行时判断指针指向的对象,并自动调用相应的函数。

修饰析构函数

虚析构函数在调用时调用对象的析构函数,这样就不会出现像有的数据成员没有销毁导致内存泄露的问题或者程序直接崩溃

复制代码
class GrandFather {
public:
	GrandFather() {std::cout << "construct grandfather" << std::endl;}
	~GrandFather() {std::cout << "destruct grandfather" << std::endl;}
};

class Father:public GrandFather{
public:
	Father() {std::cout << "construct father" << std::endl;}
	~Father() {std::cout << "destruct father" << std::endl;}
};

class Son:public Father{
public:
	Son() {std::cout << "construct son" << std::endl;}
	~Son() {std::cout << "destruct son" << std::endl;}
};

int main() {
	Father *f = new Son();
	delete f;
	return 0;
}
// 结果:
construct grandfather
construct father
construct son
destruct father
destruct grandfather

没有调用son的析构函数,当将Father或者GrandFather其中一个的析构函数修改为virtual后输出

construct grandfather
construct father
construct son
destruct son
destruct father
destruct grandfather

纯虚函数

纯虚函数的定义是在虚函数的后面加一个=0。定义了纯虚函数的类是一个抽象类。

纯虚函数的作用是在基类中声明虚函数,但在基类中不提供具体的实现。它要求任何派生类都要定义自己的实现方法,这样就可以保证所有派生类都有具体的实现。纯虚函数的目的在于,使派生类仅仅继承函数的接口,而不是具体的实现。这样,类可以无法为纯虚函数提供一个合理的默认实现。

复制代码
virtual void func() = 0;

纯虚函数需要注意这几点:

1.定义了纯虚函数的类不能够实例化,也就是不能够创建对象

2.继承了含有纯虚函数的父类的子类如果没有实现纯虚函数也不能够实例化

修饰继承性

一个类继承两个或者更多的父类,但是这些父类里又有一些有共同的父类

复制代码
class GrandFather {
public:
	GrandFather() {std::cout << "construct grandfather" << std::endl;}
	~GrandFather() {std::cout << "destruct grandfather" << std::endl;}
};

class Father1:virtual public GrandFather{
public:
	Father1() {std::cout << "construct father1" << std::endl;}
	~Father1() {std::cout << "destruct father1" << std::endl;}
};

class Father2:virtual public GrandFather{
public:
	Father2() {std::cout << "construct father2" << std::endl;}
	~Father2() {std::cout << "destruct father2" << std::endl;}
};

class Son:public Father1, Father2{
public:
	Son() {std::cout << "construct son" << std::endl;}
	~Son() {std::cout << "destruct son" << std::endl;}
};

int main() {
	Father *f = new Son();
	delete f;
	return 0;
}
// 结果:
construct grandfather
construct father1
construct father2
construct son
destruct son
destruct father2
destruct father1
destruct grandfather

如果没有虚继承GrandFather的时候,结果如下,能会导致程序挂掉
construct grandfather
construct father1
construct grandfather
construct father2
construct son
destruct son
destruct father2
destruct grandfather
destruct father1
destruct grandfather
相关推荐
王老师青少年编程1 小时前
2025年【江苏“信息与未来”编程思维】真题及题解(T2:坐标变换)
c++·题解·真题·坐标变换·编程思维·江苏·信息与未来
西门吹-禅4 小时前
java springboot N+1问题
java·开发语言·spring boot
skywalk81634 小时前
设计并实现段言的 C FFI 绑定机制 @Trae
c语言·开发语言·python·编程
2023自学中5 小时前
C++ 内存追踪器
linux·c++
IT笔记6 小时前
【Rust】Rust Match 模式匹配详解
java·开发语言·rust
2zcode6 小时前
免费开源项目文档:基于MATLAB卷积神经网络的口罩佩戴检测系统
开发语言·matlab·cnn
逝水无殇6 小时前
C# 运算符重载详解
开发语言·后端·c#
TPBoreas7 小时前
配置信息防泄露方案:.env 环境隔离详解(dotenv-java)
java·开发语言
敲代码的嘎仔7 小时前
实习日志day6--实习日志day6--title命名规范化&businessType纠正&补充缺失的@Log注解&报警与通信模块补充&产出阶段总结文档
java·开发语言·人工智能·git·python·实习·大二
江华森7 小时前
Python 实现高德地图找房(一):环境搭建与数据爬虫
开发语言·爬虫·python