前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >再探虚函数

再探虚函数

作者头像
看、未来
发布2021-10-09 14:25:35
8540
发布2021-10-09 14:25:35
举报

Q1:C++的多态如何实现

静态多态:

也称为编译期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。

重载函数的关键是函数参数列表——也称函数特征标。包括:函数的参数数目和类型,以及参数的排列顺序。所以,重载函数与返回值,参数名无关。

以下这种方式的重载是错误的,这要跟C++的编译后的函数名扯上关系了。

代码语言:javascript
复制
void print(const char* str,int width);
int print(const char* str,int width);

静态多态有两种实现方式:

代码语言:javascript
复制
函数重载:包括普通函数的重载和成员函数的重载
函数模板的使用

动态多态

在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据所指对象的实际类型来调用相应的函数,如果对象类型是派生类,就调用派生类的函数,如果对象类型是基类,就调用基类的函数。

必须是虚函数(派生类一定要重写基类中的虚函数)

在这里插入图片描述
在这里插入图片描述

Q2:什么是纯虚函数,与虚函数的区别

1、定义一个函数为虚函数,不代表函数为不被实现的函数。定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。 定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。

2、纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加"=0"

3、声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例,它必须在继承类中重新声明函数。

4、定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。 (这句话刚开始还真没反应过来,也是啊,基类都不能初始化对象了,还怎么去调用基类方法啊)


Q3:抽象基类派生类对象可以调用基类方法吗?

如果说,抽象类无法实例化对象,那就无法使用对象方法了嘛。 但是,那只是用户无法实例化罢了,,,

代码语言:javascript
复制
#include<string.h>
#include<iostream>

using namespace std;

class A {
public:
	A(){
		cout << "A" << endl;
	}

	virtual void v() = 0;
	void show() {
		cout << "A" << endl;
	}
};

class B :public A {
public:

	B(){
		cout << "B" << endl;
	}

	void v() {
		cout << "B" << endl;
	}
};

int main() {
	A* a = new B();
	B* b = new B();

	//a->show();
	//b->v();
}
代码语言:javascript
复制
A
B
A
B

Q4:抽象基类为什么不能创建对象?

那我现在有一个抽象类的对象,我要调用接口,调用哪个?


Q5:基类的析构函数为什么要定义成虚函数?

只有在基类析构函数定义为虚函数时,调用操作符delete销毁指向对象的基类指针时,才能准确调用派生类的析构函数(从该级向上按序调用虚函数),才能准确销毁数据。

代码语言:javascript
复制
#include<string.h>
#include<iostream>

using namespace std;

class A {
public:
	A(){
		cout << "A" << endl;
	}

	~A() {
		cout << "~A" << endl;
	 }

	void show() {
		cout << "A" << endl;
	}
};


class B :public A {
public:

	B(){
		cout << "B" << endl;
	}

	~B() {
		cout << "~B" << endl;
	}

	void v() {
		cout << "B" << endl;
	}
};

int main() {
	
	A* a = new B();

	delete(a);
	return 0;
}

没有虚析构的时候:

代码语言:javascript
复制
A
B
~A

有虚析构的时候:

代码语言:javascript
复制
A
B
~B
~A

Q6:析构函数可以使纯虚函数吗?

编译过不了。


Q7:构造函数和析构函数可以调用虚函数吗,为什么?

因为父类对象会在子类之前进行构造,此时子类部分的数据成员还未初始化,因此调用子类的虚函数时不安全的,故而C++不会进行动态联编;

析构函数是用来销毁一个对象的,在销毁一个对象时,先调用子类的析构函数,然后再调用基类的析构函数。所以在调用基类的析构函数时,派生类对象的数据成员已经销毁,这个时候再调用子类的虚函数没有任何意义。


Q8:静态函数能定义为虚函数吗?

1、static成员不属于任何类对象或类实例,所以即使给此函数加上virutal也是没有任何意义的。

2、静态与非静态成员函数之间有一个主要的区别,那就是静态成员函数没有this指针。

虚函数依靠vptr和vtable来处理。vptr是一个指针,在类的构造函数中创建生成,并且只能用this指针来访问它,因为它是类的一个成员,并且vptr指向保存虚函数地址的vtable.对于静态成员函数,它没有this指针,所以无法访问vptr。


Q8:基类的虚函数表存放在内存的什么区,虚表指针vptr的初始化时间?

1、 虚函数表是class specific的,也就是针对一个类来说的,这里有点像一个类里面的staic成员变量,即它是属于一个类所有对象的,不是属于某一个对象特有的,是一个类所有对象共有的。 2、虚函数表类似一个数组,类对象中存储vptr指针,指向虚函数表。 3、虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表。所以不在堆中。

推测在全局变量区。

请看下面的程序,该程序演示了多态类对象存储空间的大小:

代码语言:javascript
复制
#include <iostream>
using namespace std;
class A
{
public:
    int i;
    virtual void func() {}
    virtual void func2() {}
};
class B : public A
{
    int j;
    void func() {}
};
int main()
{
    cout << sizeof(A) << ", " << sizeof(B);  //输出 8,12
    return 0;
}

实际上,任何有虚函数的类及其派生类的对象都包含这多出来的 4 个字节,这 4 个字节就是实现多态的关键——它位于对象存储空间的最前端,其中存放的是虚函数表的地址。

每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着该虚函数表的指针(可以认为这是由编译器自动添加到构造函数中的指令完成的)。

虚函数表是编译器生成的,程序运行时被载入内存。一个类的虚函数表中列出了该类的全部虚函数地址。例如,在上面的程序中,类 A 对象的存储空间以及虚函数表(假定类 A 还有其他虚函数)如图 1 所示。

在这里插入图片描述
在这里插入图片描述

类 B 对象的存储空间以及虚函数表(假定类 B 还有其他虚函数)如图 2 所示。

在这里插入图片描述
在这里插入图片描述

多态的函数调用语句被编译成根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的一系列指令。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/09/02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Q1:C++的多态如何实现
    • 静态多态:
      • 动态多态
      • Q2:什么是纯虚函数,与虚函数的区别
      • Q3:抽象基类派生类对象可以调用基类方法吗?
      • Q4:抽象基类为什么不能创建对象?
      • Q5:基类的析构函数为什么要定义成虚函数?
      • Q6:析构函数可以使纯虚函数吗?
      • Q7:构造函数和析构函数可以调用虚函数吗,为什么?
      • Q8:静态函数能定义为虚函数吗?
      • Q8:基类的虚函数表存放在内存的什么区,虚表指针vptr的初始化时间?
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档