C++多态深入分析!
以下分析是基于VS2010的。以后会使用G++分析看看G++如何处理多态!
// polymorphic_test.cpp : 定义控制台应用程序的入口点。
// /**
特别注意:实现C++多态,除了基类相关函数要声明 virtual关键字,还需要派生类的该函数签名和基类完全一致!两个条件缺一不可,否则:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual
关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual
关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
*/ #include "stdafx.h"
#include<iostream>
using namespace std; class A
{
public:
A():a(){;}
void foo() { std::cout << "BaseMember\t"; }
virtual void vfun() { std::cout << "BaseVirtual\t"; }
//private:
int a;
}; class B : public A
{
public:
B():b(){;}
void foo() { std::cout << "DerivedMember\t"; }
void vfun() { std::cout << "DerivedVirtual\t"; }
//private:
int b;
}; void test()
{
A a;
B b;
A *pa = NULL;
B *pb = NULL; while(true){
pa = &a;
sizeof(a); sizeof(b);
&a.a; &b.b; &b.a;
(int)&a.a ; (int)&b.b ;
int tt = &b - &a;
int t = (int)&b - (int)&a;
pa->foo(); // 非虚拟函数是编译器绑定,指针类型是什么,就调用该类型的成员函数!因此这里输出 BaseMember
pa->vfun(); // 运行期绑定,调用实际的类型函数!输出 BaseVirtual\t pa = &b;
pa->foo(); // 非虚拟函数是编译器绑定,指针类型是什么,就调用该类型的成员函数!由于指针类型是A*,因此调用A的成员函数。
// 因此输出 BaseMember
pa->vfun(); // 运行期绑定,调用实际类型的成员函数。因此输出 DerivedVirtual\t
//return 0;
std::cout << std::endl; pb = &b;
pb->foo(); pb->vfun(); // 这里输出 DerivedMember\t, DerivedVirtual\t .好理解。 pb = (B*)&a;
pb->foo(); // 这里注意:非虚拟函数编译期间绑定, pb的类型是B*, 因此调用B的成员函数。输出 DerivedMember\t
pb->vfun(); // 虚拟函数,调用实际类型的函数,现在pb指向的&a, 因此调用A的成员函数,输出 BaseVirtual\t std::cout << "\n" << std::endl;
} } int _tmain(int argc, _TCHAR* argv[])
{
test();
return ;
}
根据调试信息,观察到的对象内存地址!
执行pa=&a之后:

指向pa=&b之后:

根据内存地址,汇出的对象内存布局. (不太会用word,画的太乱了。抱歉!)

根据该内存布局,我们可以总结如下:
1. 派生类对象也有基类继承成员的独立拷贝,并非如《深入C++对象模型》所说的“派生类的继承自基类的成员是依附于基类对象“
2. 每个对象都在栈地址上分配(没有使用new)。对象间是从高地址到低地址分配,所以a对象的起始地址大于b对象的;而在对象内部数据成员之间,是从低地址到高地址开始分配,所以b.a地址要高于b.b地址。
3. 假如存在vptr,那么vptr在对象的最开始处分配(vptr是对象内存布局的第一个成员).
4. 程序运行过程中,假如ptr指向不同的对象。那么调用虚函数时,会通过vptr+offset找到虚函数的入口地址。(比如,pa指向&a时,那么pa->vfun(),就是通过a对象的vptr找到vfun的地址,所以是调用A::vfun() ;当pa = &pb执行后,pa此时指向&pb,然后据此得到b对象的vptr+offset调用B::vfun()).
5. 非虚函数,是编译期间绑定,指针实际类型是什么,就调用该类型的成员函数,与运行期所指向对象无关。因为,因为pa的类型是A*,所以pa->foo()总是调用A::foo(),同理pb->foo()总是调用B::foo().
程序运行结果:


C++多态深入分析!的更多相关文章
- Java中的多态
1.多态:具有表现多种形态的能力的特征 父类: public abstract class Animal {public abstract void Say();} 子类: public class ...
- C# 工厂模式+虚方法(接口、抽象方法)实现多态
面向对象语言的三大特征之一就是多态,听起来多态比较抽象,简而言之就是同一行为针对不同对象得到不同的结果,同一对象,在不同的环境下得到不同的状态. 实例说明: 业务需求:实现一个打开文件的控制台程序的d ...
- C#非常重要基础之多态
前几天看了一位同志的博客,写的是关于他自己去支付宝面试的经历.过程大体是这样的:问答的时候,前面部分,作者都应答如流,说起自己经验如何之丰富,最后面试官问了作者一个问题:请简述多态的概念和作用.结果这 ...
- C++多态详解
多态是面向对象的程序设计的关键技术.多态:调用同一个函数名,可以根据需要但实现不同的功能.多态体现在两个方面,我们以前学过的编译时的多态性(函数重载)和现在我们这一章将要学习的运行时的多态性(虚函数) ...
- 深入分析Spring 与 Spring MVC容器
1 Spring MVC WEB配置 Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext, ...
- 【那些年关于java多态应用】
1.多态:具有表现多种形态的能力的特征 父类: public abstract class Animal { public abstract void Say();} 子类: public class ...
- JAVA多态
多态是指当系统A访问系统B的服务时,系统B可以通过多种方式来提供服务,而这一切对系统A是透明的.比如动物园的饲养员能够给各种各样的动物喂食.下图显示了饲养员Feeder,食物Food和动物Animal ...
- C#多态“说来也说”——逻辑层BLL中的多态使用
本文版权归博客园和作者吴双本人共同所有.欢迎转载,转载和爬虫请注明原文地址 http://www.cnblogs.com/tdws/p/5861842.html 昨天晚上,有个朋友说学了好久,依然没搞 ...
- java多态的理解
面向对象语言中的类有三个特征,封装.继承.多态.封装与继承很好理解,那什么是多态呢? 1.什么是多态? 多态的定义:指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而采用多种不同 ...
随机推荐
- SSM框架之批量增加示例(同步请求jsp视图解析)
准备环境:SSM框架+JDK8/JDK7+MySQL5.7+MAVEN3以上+Tomcat8/7应用服务器 示例说明: 分发给用户优惠券,通过checkbox选中批量分发,对应也就是批量增加. 对于公 ...
- Python之django自带的分页功能
前端页面: <div class="col-sm-6"> <div class="dataTables_paginate paging_simple_n ...
- Python自动化之迭代器不能在迭代的时候更改值
除列表外的其他序列都是不可变的, 所以危险就发生在这里. 一个序列的迭代器只是记录你当前到达第多少个元素, 所以如果你在迭代时改变了元素, 更新会立即反映到你所迭代的条目上.在迭代字典的 key 时, ...
- 【转】android SystemUI 流程分析
android4 SystemUI 流程分析 什么是SystemUI? 对于Phone来说SystemUI指的是:StatusBar(状态栏).NavigationBar(导航栏).而对于Tablet ...
- java 内存优化
计数器pc 2.2 虚拟机栈和程序计数器一样,虚拟机栈也是线程私有的,它的生命周期与线程相同.虚拟机栈描述的是java方法执行的内存模型. 每个方法(不包含native方法)执行的同时都会创建一个栈帧 ...
- Spring源码分析(一)基本介绍
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 前言 作为一名开发人员,阅读源码是一个很好的学习方式.本文将结合< ...
- SharePoint Search 分词(WordBreaker)
[问题] 我们经常会搜索一个很长的词,例如“国際協力銀行” ,对应的搜索结果中会有关于“国際”“協力”“銀行”相关内如都会搜索到,这样的行为就是分词,那如何知道搜索内如是如何分词的呢? [解决办法] ...
- php使用urlencode对中文编码而引出的问题:urlencode和rawurlencode的区别
事件背景: 之前做h5小游戏,需要后端输出用户的相关信息给前端,输出的内容有:用户id,用户昵称等字段,使用get方式传参.后端使用PHP语言对中文昵称进行格式化编码,使用的是常用的urlencode ...
- MacOS下netstat和lsof使用的若干问题
[-= 博客目录 =-] 1-相关说明 1.1-博客介绍 1.2-netstat和lsof 2-学习过程 2.1-netstat 2.2-lsof 2.3-netstat和lsof区别和关联 3-资料 ...
- python之shutil模块详解
shutil模块 -- --High-level file operations 高级的文件操作模块. os模块提供了对目录或者文件的新建/删除/查看文件属性,还提供了对文件以及目录的路径操作.比如 ...