C++ 虚函数在基类与派生类对象间的表现及其分析
近来看了侯捷的《深入浅出MFC》,读到C++重要性质中的虚函数与多态那部分内容时,顿时有了疑惑。因为书中说了这么一句:使用“基类之指针”指向“派生类之对象”,由该指针只能调用基类所定义的函数,如果要让基类的指针使用派生类中定义的函数,就将该函数定义为虚函数。
但在“Object slicing与虚函数”这一小节给出了一个及其经典的例子,它指出,在向上(即向基类)强制转型时,会造成对象内容的被切割。
下面用示例进行说明:
#include "stdafx.h"
#include <iostream>
using namespace std; class A
{
public:
virtual void fn(){cout<<"A fn"<<endl;}
}; class B: public A
{
public:
virtual void fn(){cout<<"B fn"<<endl;}
}; int main(int argc, char* argv[])
{
B b1;
A a1=(A)b1;
A * a2=(A*)&b1;
a1.fn();
a2->fn();
return ;
}
结果如下:
通过调试分析其内存模型如下:
可知,通过A a1=(A)b1传值时,对象中指向虚函数表的指针__vfptr值不同,但是通过A *a2=(A*)&b1传址操作时,对象中指向虚函数表的指针__vfptr值是相同的,调用的是类B对象的虚函数表中虚函数fn()。注意,对象调用的是哪个类的虚函数就要看类对象的虚函数表中的函数是哪一个。这句话也好理解,a1虚函数表中fn()是A::fn(),a2虚函数表中fn()是B::fn(),所以出现以上结果。
在如下包含父类的父类的继承中:
class A
{
public:
virtual void fn(){cout<<"A fn"<<endl;}
}; class B: public A
{
public:
virtual void fn(){cout<<"B fn"<<endl;}
}; class C: public B
{
public:
};
C c;
A a1=(A)c;
A * a2=(A*)&c;
a1.fn();
a2->fn();
这时,a1虚函数表中fn()为A::fn(),a2虚函数表中fn()为B::fn(),因为类C对象的虚函数表中fn()为B::fn(),a2中指向虚函数表的指针值与类C对象指向虚函数表的指针值相同。
侯捷的书中有句话是这么对它进行解释的:由于(A)b1.fn()是传值而非传地址操作,编译器以所谓的拷贝构造函数把A对象内容复制了一份,使得b1的虚函数表内容与A对象的虚函数表内容相同。
看来,得多读圣贤书,读懂读透啊!
C++ 虚函数在基类与派生类对象间的表现及其分析的更多相关文章
- (转) C++中基类和派生类之间的同名函数的重载问题
下面有关派生类与基类中存在同名函数 fn: class A { public: void fn() {} void fn(int a) {} }; class B : public A { publi ...
- 构造函数为什么不能为虚函数 & 基类的析构函数为什么要为虚函数
一.构造函数为什么不能为虚函数 1. 从存储空间角度,虚函数相应一个指向vtable虚函数表的指针,这大家都知道,但是这个指向vtable的指针事实上是存储在对象的内存空间的.问题出来了,假设构造函数 ...
- C++中虚基类在派生类中的内存布局
今天重温C++的知识,当看到虚基类这点的时候,那时候也没有太过追究,就是知道虚基类是消除了类继承之间的二义性问题而已,可是很是好奇,它是怎么消除的,内存布局是怎么分配的呢?于是就深入研究了一下,具体的 ...
- OOP2(虚函数/抽象基类/访问控制与继承)
通常情况下,如果我们不适用某个函数,则无需为该函数提供定义.但我们必须为每个虚函数都提供定义而不管它是否被用到了,这因为连编译器也无法确定到底会适用哪个虚函数 对虚函数的调用可能在运行时才被解析: 当 ...
- 详解C++中基类与派生类的转换以及虚基类
很详细!转载链接 C++基类与派生类的转换在公用继承.私有继承和保护继承中,只有公用继承能较好地保留基类的特征,它保留了除构造函数和析构函数以外的基类所有成员,基类的公用或保护成员的访问权限在派生类中 ...
- 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)
[源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...
- C++基类和派生类之间的转换
本文讲解内容的前提是派生类继承基类的方式是公有继承,关键字public 以下程序为讲解用例. #include<iostream> using namespace std; class A ...
- OOP1(定义基类和派生类)
面向对象程序设计基于三个基本概念:数据抽象,继承和动态绑定 数据抽象是一种依赖于接口和实现分离的编程技术.继承和动态绑定对程序的编号有两方面的影响:一是我们可以更容易地定义与其它类相似但不完全相同的类 ...
- c++中基类与派生类中隐含的this指针的分析
先不要看结果,看一下你是否真正了解了this指针? #include<iostream> using namespace std; class Parent{ public: int x; ...
随机推荐
- POJ3680_Intervals
给你若干个区间,每个区间有一个权值,你可以选出某些区间,使得在保证没有任何一段的覆盖次数超过k的前提下,总的权值最大. 这个建模真的十分神奇,赞一个. 对于给出的每一个区间,离散化,最终我们可以知道所 ...
- FastDFS基本结构(转)
0.简介 FastDFS是基于互联网应用的开源分布式文件系统,主要用于大中型网站存储资源文件,如图片.文档.音频.视频等.FastDFS采用类似GFS的架构,用纯C语言实现,支持Linux.FreeB ...
- 获取root权限
1.用root建立一个普通用户mary,并切换到mary. < 2.我们首先测试一下当前用户的权限 3.进入到/tmp,新建目录abc. 4.执行下列相关命令.并保证最后一行后面的两块红色部分为 ...
- 无线安全专题_破解篇02--kali破解pin码
最近项目有点紧,所以本应该上周发的文章,拖到了本周三,在此说声抱歉.无线安全专题,我打算系统地写六个部分,分别为破解篇,攻击篇,欺骗篇,路由篇,移动篇和蓝牙篇,当然在发布的过程中,可能还会掺杂着发布f ...
- 盘点国内网站常用的一些 CDN 公共库加速服务
CDN公共库是指将常用的JS库存放在CDN节点,以方便广大开发者直接调用.与将JS库存放在服务器单机上相比,CDN公共库更加稳定.高速.一 般的CDN公共库都会包含全球所有最流行的开源JavaScri ...
- ASP.NET的一次奇遇:UserControl写成Control引发的w3wp进程崩溃
昨天在写代码中一不小心将UserControl写成了Control,将原来应该继承自System.Web.UI.UserControl的用户控件,比如下面的BlogStats: <%@ Cont ...
- C语言 栈 顺序结构 实现
一个能够自动扩容的顺序结构的栈 ArrStack 实例 (GCC编译). /** * @brief C语言实现的顺序结构类型的栈 * @author wid * @date 2013-10-29 * ...
- 图书馆管理系统—NABCD模型竞争性需求分析
本次课程设计设计题目:“图书管理系统”主要目的是利用数据库软件编制一个管理软件,用以实现图书.读者以及日常工作等多项管理.同时对整个系统的分析.设计过程给出一个完整论证. 1>N(Need 需求 ...
- 在stackoverflow上使用markdown
stackoverflow流派的markdown. Code and Preformatted Text 缩进四个空格,就可以写代码片段了 def hello(): print "hello ...
- 安卓App和java通信实例
服务器:放在电脑上运行的java文件 import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.I ...