还原virtual函数的本质-----C++

当你每次看到C++类中声明一个virtual函数,特别是看到了一个virtual的虚构函数。你知道它的意思吗?你肯定会毫不犹豫的回答:不就是多态么。。。在运行时确定具体的行为么。。。完全正确,但这里我要讲的不只是这些东西。

 有些类需要虚函数,有些不需要虚函数。这是为什么,一般你看到的类如果有一个虚析构函数,那么这个类中应该会有至少一个是虚函数的。。这是为什么呢??如果我们类中没有用其他虚函数的话,你创建了这个也是多余的,而且会增加类对象的大小。。说这些纯理论的东西,也许大家不知所云。。下面我就给例子来验证。。

1:

 

class A
{
public:A(){};
//	virtual ~A(){};~A();
};
void main()
{A a;cout<<sizeof(a)<<endl;
}


结果为1。这个1应该是编译器自己为它加上的。。哪怕你不在类中不写任何东西,它也是1;例如;

 

 

 

class A
{};
void main()
{A a;cout<<sizeof(a)<<endl;
}


如果你把析构函数声明为虚函数。。如:

 

 

2:

class A
{
public:
A(){};
virtual ~A(){};
//~A();
};
void main()
{


A a;
cout<<sizeof(a)<<endl;
}

结果是4。先不说这是为什么。。


然后还是说一下关于虚函数基础的东西(多态)吧,也给个例子:

 

#include<iostream>
#include<string>
using namespace std;class Base
{
public:Base();virtual ~Base();virtual  void test();
private :int count;
};
Base::Base(){cout<<"Base部分创建了"<<endl;
}
Base::~Base(){cout<<"Base部分被销毁了"<<endl;
}
void Base::test()
{cout<<"Base Test"<<endl;}class Derive1:public Base
{
public:Derive1();virtual  ~Derive1();void test();};
Derive1::Derive1(){cout<<"子类部分创建了"<<endl;
}
Derive1::~Derive1(){cout<<"子类部分被销毁了"<<endl;
}
void Derive1::test()
{cout<<"Derive1 Test"<<endl;
}void main()
{Base* d1=new Derive1();d1->test();delete d1;}


 

 


由此看见,当通过声明一个父类指针并且让它指向一个子类的对象,在子对象创建的时候,会先去调用父类的构造函数,然后再是自己的构造函数,当通过父类指针去调用一个虚函数test()时,它实际上回去调用子类的test()函数,这是为什么呢,它肯定有什么信息让它这样做吗。。这个信息肯定是子类对象给它的。。这个信息就是虚函数指针(vptr),它指向一个虚函数表(vtbl),这个虚函数表其实就是包含了这个类的所有虚函数的函数名(函数指针),每个类就只包含了那一个虚函数指针和它的一些成员变量。这下可以解释上面为什么是1,为什么是4了。。

 在win32的机器上,每个指针是4字节。刚才也提到每个类的大小取决于两部分,一个是成员变量,一个是虚函数指针而且有且只有一个,在例子一中,因为没有成员变量,而有一个虚函数---析构函数,此时肯定会有一个虚函数指针,所以是4。。 其实刚才也就同时说清楚了多态的本质,就是子对象的虚函数指针给出了这个信息,父类指针才知道去执行哪个函数。。

最后一个问题:为什么析构函数要声明为虚函数呢?(当至少有一个为虚函数的时候)

从刚才的那个结果也可以看出,当我们delete那个指针的时候,会发生析构,而且这个过程是从子类到父类的顺序进行。假如此时析构函数不为虚函数,父类指针也就不知道去执行子类的析构函数。。也就不会去释放子对象的那部分内存,造成内存泄漏。。例如:(这里我们只是对上一段代码进行修改,去掉了父类中的virtual):

 

#include<iostream>
#include<string>
using namespace std;class Base
{
public:Base();~Base();virtual  void test();
private :int count;
};
Base::Base(){cout<<"Base部分创建了"<<endl;
}
Base::~Base(){cout<<"Base部分被销毁了"<<endl;
}
void Base::test()
{cout<<"Base Test"<<endl;}class Derive1:public Base
{
public:Derive1();~Derive1();void test();};
Derive1::Derive1(){cout<<"子类部分创建了"<<endl;
}
Derive1::~Derive1(){cout<<"子类部分被销毁了"<<endl;
}
void Derive1::test()
{cout<<"Derive1 Test"<<endl;
}void main()
{Base* d1=new Derive1();d1->test();delete d1;}

 

 

所以当至少有一个虚函数的话,我们也要把它的析构函数声明为virtual。(插一句:有些人会说你子类中的那个函数哪里是虚函数哦,我没看到virtual 啊。。其实C++允许我们这样做,重写父类的虚函数,不是必须要声明出来的。)

总结:一个类有了虚函数,是为了成为一个基类,如果不是这样的话,那么父类中的任何函数都没有必要是虚函数,甚至会增加类的大小。多态告诉了我们这点。。一旦成为了基类,那么就要把析构函数声明为一个虚函数。。

好了,虚函数的内容就Over了。。。。。

 

转载于:https://www.cnblogs.com/pangblog/p/3258031.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++实践参考——数组类运算的实现

【项目-数组类运算的实现】   设计数组类Array&#xff0c;为了实现测试函数中要求的功能&#xff0c;请补足相关的函数&#xff08;构造、析构函数&#xff09;和运算符重载的函数。   实现策略提示&#xff1a;可以将测试函数中的语句加上注释&#xff0c;取消一句的注…

MySQL支持的四种索引_Mysql常见四种索引的使用

提到mysql优化&#xff0c;索引优化是必不可少的。其中一种优化方式 ——索引优化&#xff0c;添加合适的索引能够让项目的并发能力和抗压能力得到明显的提升。我们知道项目性能的瓶颈主要是在"查(select)"语句&#xff0c;要提升"查"这一性能&#xff0c;…

java调用、执行groovy代码

java调用、执行groovy代码 1: package test; 2: 3: import javax.script.ScriptEngine; 4: import javax.script.ScriptEngineManager; 5: import javax.script.ScriptException; 6: 7: public class ScriptExcuteUtil { 8: 9: public ScriptExcuteUtil() { 10: 11…

AGS Server 10.1 切图工具

在AGS Sever中很重要的功能就是地图缓存的制作&#xff0c;安装AGS Sever会在catalog中增加相关的工具箱&#xff0c;利用这些工具可以制作、删除、更新切片 一、Convert map server cache storage format(转换服务缓存的存储格式) 利用该工具可以快速的切换地图缓存的格式&…

0511 backlog 项目管理

SCRUM 这次的作业就是确定SCRUM的计划&#xff0c;确定sprint backlog的一个冲刺周期&#xff0c;而这个周期是两个星期。争取在两周内发布1.0版本。 本次作业以网站构建为主&#xff1a; ID NAMEIMPESTHOW TO DONOTES1首页99小时用户登录网站也可以看游客的推广的内容&am…

swing和MySQL登录注册_JavaSwing+Mysql实现简单的登录界面+用户是否存在验证

原生Javamysql登录验证clientlogin.java功能&#xff1a;实现登录页面&#xff0c;与服务端传来的数据验证package LoginRegister;import java.awt.Container;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.util.regex.Pattern;import …

4月27日微软云训练营活动-现场图集

1.签到 2.到场同学&#xff0c;这一天是工作日&#xff0c;但是人气依然很火。 转载于:https://www.cnblogs.com/finehappy/p/3262296.html

vim编程 插入 保存不退出 保存退出 退出不保存 另存为其他文件名 保存覆盖现有文件...

---恢复内容开始--- 在xshell里写代码&#xff0c;如果需要编辑代码&#xff0c;可以输入 vim xxx.py ,进入vi&#xff4d;编辑界面 这里的xxx.py表示 python的存储文件&#xff0c;后缀名是.py。&#xff11;&#xff0e;插入字符 输入 i 可键入或者shift i&#xff12;&…

wdcp mysql密码_wdcp默认的mysql密码是多少?

重置mysql root密码方法一、Windows环境的找回方法&#xff1a;1、打开cmd命令窗口&#xff0c;先输入命令&#xff1a;net stop mysql 回车 #关闭mysql服务。进入到D:\SOFT_PHP_PACKAGE\mysql\bin\ 目录下&#xff0c;执行mysqld --skip-grant-tables 启动MySQL Server (如果报…

Java中内存中的Heap、Stack与程序运行的关系

堆和栈的内存管理 栈的内存管理是顺序分配的&#xff0c;而且定长&#xff0c;不存在内存回收问题&#xff1b;而堆 则是随机分配内存&#xff0c;不定长度&#xff0c;存在内存分配和回收的问题&#xff1b;堆内存和栈内存的区别可以用如下的比喻来看出&#xff1a;使用堆内存…

ASP.NET Core的配置(5):配置的同步[设计篇]

本节所谓的“配置同步”主要体现在两个方面&#xff1a;其一&#xff0c;如何监控配置源并在其变化的时候自动加载其数据&#xff0c;其目的是让应用中通过Configuration对象承载的配置与配置源的数据同步&#xff1b;其二、当Configuration对象承载的配置放生变换的时候如何向…

mysql 5.6 linux安装配置_linux手动安装配置mysql5.6

1.准备工作①官网下载&#xff1a;https://dev.mysql.com/downloads/mysql/5.6.html#downloads下载之后上传到服务器。②创建linux组用户groupadd mysqluseradd -g mysql mysql2.安装①解压&#xff0c;比如放到了/usr/local/,进入到该目录下&#xff0c;进行用z解压gz包&#…

Winodws Socket I/O模型的整理

Winodws Socket I/O模型的整理大致分以五种.其中Overlapped I/O模型是有两种实现方法. 一&#xff1a;select模型二&#xff1a;WSAAsyncSelect模型三&#xff1a;WSAEventSelect模型四&#xff1a;Overlapped I/O 事件通知模型/完成例程模型五&#xff1a;完成端口IOCP模型 这…

KVM安装Windows Server 2008 R2使用virtio硬盘

在上一篇文章中&#xff0c;我们介绍了使用IDE硬盘来安装Windows Server 2008 R2,这篇文章我们来介绍使用virtio硬盘来安装Windows Server 2008 R2。 说明&#xff1a;KVM默认使用的硬盘格式为virtio。 使用virtio接口的硬盘&#xff0c;我们必须加载virtio硬盘驱动。如果不加载…

Sublime Text 2 入门及技巧

看了 Nettuts 对 Sublime Text 2 的介绍&#xff0c;立刻就兴奋了&#xff0c;诚如作者 Jeffrey Way 所说&#xff1a;“《永远的毁灭公爵》都发布了&#xff0c;TextMate 2 还没发”&#xff0c;你还能指望它么&#xff1f;TextMate 开发者的消极态度已经无法让人忍受了。而作…

跨域名,服务器登录 .

编辑器加载中... function SingleSignOn() {//只能用脚本改变指定 Form 提交的对象 document.getElementById("form1").action"http://。。。。。/WebApp/UsersLogin.aspx"; //把隐藏控件 __VIEWSTATE 中的值变更为 LoginTransfer…

YII 配置文件

用YIIFramework的库开发 Java代码 .... Yii::createWebApplication($config); //没有run Yii::import&#xff08;class1&#xff0c;true&#xff09;,在将class1类文件路径存储时&#xff0c;同时include该文件 注意&#xff1a;你也可以将配置文件分为多个文件&#xff0…

mysql 备份到别的机器_物理拷贝备份mysql到其他机器上恢复

经常会遇到mysql数据要迁徙的情况 &#xff0c;逻辑导出是可以的 但是就是太慢 第三方工具也同样可以用 也是操作麻烦&#xff0c;最简单粗暴的方式就是直接拷贝物理文件来的最快 也很简单 当然 前提是远程服务器和目标服务器是相同的文件系统和mysql版本&#xff0c;不然还是没…

Hibernate映射关系总结篇

又从头学习了一遍hibernate的映射关系&#xff0c;每一次都会有新的收获&#xff0c;总是感觉自己还是不会hibernate。单从配置上说&#xff1a;知其然不知其所以然&#xff0c;马上就要找工作的人了&#xff0c;很是为自己担心呀&#xff01;&#xff01; 众所周知&#xff0c…

iOS内存管理(ARC,MRC)

iOS内存管理方式&#xff1a; ARC Automatic Reference Counting 自动引用计数 MRC Manual Reference Counting 手动引用计数 更改管理方式&#xff1a; 内存管理的问题&#xff1a; 1、内存泄露&#xff1a;不再需要的对象没有释放。 2、野指针&#xff1a;正在使用的对象提前…