内存角度探寻C++面向对象 之 继承、多态
一,简单继承:
#include <iostream> class TableTennisPlayer
{
private:
int id;
public:
TableTennisPlayer(int id);
void sayHi();
}; TableTennisPlayer::TableTennisPlayer(int n)
{
id = n;
} void TableTennisPlayer::sayHi()
{
std::cout << "Hi,I am player" << id << std::endl;
} class MasterPlayer :public TableTennisPlayer
{
private:
int id;
public:
MasterPlayer(int id);
void fuck();
}; MasterPlayer::MasterPlayer(int n) :TableTennisPlayer(n)
{
id = n;
} void MasterPlayer::fuck()
{
std::cout << "fuck you" << std::endl;
} int main(void)
{
using std::cout;
using std::cin;
TableTennisPlayer player(1);
MasterPlayer master(2);
player.sayHi();
master.sayHi();
master.fuck();
cout << sizeof(player) << std::endl;
cout << sizeof(master) << std::endl;
cin.get();
}
父类成员变量id在构造函数初始化。
子类也有成员变量id,在子类构造函数初始化。
反汇编代码看出,子类构造函数初始化子类时首先调用父类构造函数初始化父类--具体是初始化父类成员变量。
所以:
这是父类在内存中的存储 00 00 00 01 占4字节

这是子类在父类的存储:
00 00 00 02 00 00 00 02 占8字节,前四字节是父类成员变量,后四字节是子类成员变量

由此看出,子类对父类是包含关系。具体是指包含父类成员变量。另外,构造函数没什么特殊的。其实就是类建立时自动调用的函数。去初始化类成员,也就是为成员变量申请内存。子类初始化,先调用父类构造方法初始化,父类成员变量。然后调用自己的,初始化自己成员变量。
二,虚函数
由于C++中存在指针的概念。所以,必须引出虚函数。这一点你如果不从内存角度思考,去真正理解C++中对象、以及指向对象的指针的概念是什么,你是无法真正理解虚函数的!!!
如果一个指针指向子类。那么:
#include<iostream>
using namespace std;
class A
{
public:
void print()
{
cout<<"This is A"<<endl;
}
}; class B : public A
{
public:
void print()
{
cout<<"This is B"<<endl;
}
}; int main()
{
A a;
B b;
a.print();
b.print();
return ;
}
运行结果:指向子类的指针,调用的却是父类的print函数。
这便是引入虚函数概念的原因:
#include<iostream>
using namespace std;
class A
{
private:
int a = ;
public:
virtual void print()
{
cout << "This is A" << endl;
}
}; class B : public A
{
private:
int b = ;
public:
virtual void print()
{
cout << "This is B" << endl;
}
}; class C : public B
{
private:
int c = ;
public:
virtual void print()
{
cout << "This is C" << endl;
}
}; int main()
{
A a;
B b;
C c;
A *p1 = &a;
A *p2 = &b;
A *p3 = &c;
p1->print();
p2->print();
p3->print();
cin.get();
return ;
}

所以利用指针来操作的话,虽然调用的是指向子对象的指针,其实那里存储的是子对象里包含着的父对象的空间。
加上virtual修饰符后,则是完全另一种结果。
带有虚函数的对象是另外一种初始化方式:在构造函数中会将this指针指向虚函数表(对象内存地址里首先保存的是虚函数表)。
也就是说:带有虚函数的对象,对象地址首先保存的是,指向虚函数表的指针(地址),然后才是类成员变量。
子类继承了,带有虚函数的父类。对象地址首先保存的是,指向虚函数表的指针(地址),然后是父类成员变量,然后才是子类成员变量。
三、多重继承
继续上面的例子,C继承B,B继承A(A为虚函数)。那么C在内存中是什么样子呢?前四个字节是虚函数表指针,然后是A类成员变量,然后是B类成员变量,最后才是自己的成员变量。
四、多继承
如下代码所示,类C继承类B、类A
#include<iostream>
using namespace std;
class A
{
private:
int a = ;
public:
virtual void print()
{
cout << "This is A" << endl;
}
}; class B
{
private:
int b = ;
public:
virtual void print()
{
cout << "This is B" << endl;
}
}; class C : public B,public A
{
private:
int c = ;
public:
virtual void print()
{
cout << "This is C" << endl;
}
}; int main()
{
C c;
A *p = &c;
p->print();
cin.get();
return ;
}

由反汇编结果可知,内存中存储结果为:指向虚函数表的指针、类B成员变量、指向虚函数表的指针、类A成员变量、最后是自己的成员变量。
五,总结
由上面几个例子可以知道。
1 C++为了实现面向对象语言中的“向上转型”,特别引入了虚函数的概念。为何如此?因为C++有指针!!!!!!
Java是不需要的,因为java没有指针。
2 虚函数表具体是什么样的?具体可以看这篇博客:http://blog.csdn.net/haoel/article/details/1948051/
类似这张图,虚函数表中,父类的虚函数在子类虚函数前面。
内存角度探寻C++面向对象 之 继承、多态的更多相关文章
- php面向对象 封装继承多态 接口、重载、抽象类、最终类总结
1.面向对象 封装继承多态 接口.重载.抽象类.最终类 面向对象 封装继承多态 首先,在解释面向对象之前先解释下什么是面向对象? [面向对象]1.什么是类? 具有相同属性(特征)和方法(行为)的一 ...
- python面向对象(封装,继承,多态)
python面向对象(封装,继承,多态) 学习完本篇,你将会深入掌握 如何封装一个优雅的借口 python是如何实现继承 python的多态 封装 含义: 1.把对象的属性和方法结合成一个独立的单位, ...
- Java基础——面向对象(封装——继承——多态 )
对象 对象: 是类的实例(实现世界中 真 实存在的一切事物 可以称为对象) 类: 类是对象的抽象描述 步骤: 1.定义一个类 (用于 描述人:) ( * 人:有特征和行为) 2.根据类 创建对象 -- ...
- python面向对象之继承/多态/封装
老师说,按继承/多态/封装这个顺序来讲. 子类使用父类的方法: #!/usr/bin/env python # coding:utf-8 class Vehicle: def __init__(sel ...
- python基础语法15 面向对象2 继承,多态,继承json模块中JSONEncoder,并派生出新的功能
继承 1.什么是继承? 继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类. - 在Python中,一个子类可以继承多个父类.(面试可能会问) - 在其它语言中,一个子类只 ...
- java面向对象(封装-继承-多态)
框架图 理解面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是一种思想 面向过程强调的是功能行为 面向对象将功能封装进对象,强调具备了功能的对象. 面向对象是基于面向过程的. 面向对象的特点 ...
- -1-2 java 面向对象基本概念 封装继承多态 变量 this super static 静态变量 匿名对象 值传递 初始化过程 代码块 final关键字 抽象类 接口 区别 多态 包 访问权限 内部类 匿名内部类 == 与 equal
java是纯粹的面向对象的语言 也就是万事万物皆是对象 程序是对象的集合,他们通过发送消息来相互通信 每个对象都有自己的由其他的对象所构建的存储,也就是对象可以包含对象 每个对象都有它的类型 也就是 ...
- day25 面向对象继承,多态,
这两天所学的都是面向对象,后面还有几天也是它,面向对象主要有三个大的模块,封装,继承,多态,(组合),昨天主要讲了面向对象的命名空间,还有组合的用法,今天是讲的继承还有继承里面所包括的钻石继承,以及多 ...
- Python 面向对象编程 继承 和多态
Python 面向对象编程 继承 和多态 一:多继承性 对于java我们熟悉的是一个类只能继承一个父类:但是对于C++ 一个子类可以有多个父亲,同样对于 Python一个类也可以有多个父亲 格式: c ...
随机推荐
- ServletContext获取的方法
ServletContext 代表当前web应用 如何获取ServletContext对象 ServletConfig对象中维护了ServletContext对象的引用,可以通过以下方式获得 Ser ...
- Codeforces Round #200 (Div. 1) D. Water Tree(dfs序加线段树)
思路: dfs序其实是很水的东西. 和树链剖分一样, 都是对树链的hash. 该题做法是:每次对子树全部赋值为1,对一个点赋值为0,查询子树最小值. 该题需要注意的是:当我们对一棵子树全都赋值为1的 ...
- response.setHeader()的用法
一秒刷新页面一次 response.setHeader("refresh","1"); 二秒跳到其他页面 response.setHeader("re ...
- Android软件设计---Dumpsys工具使用
Android中提供的dumpsys工具,用于分析Android性能.Android系统中,列出所有可用的dumpsys指令. 使用dumpsys查看memory信息: shell@aeon6735m ...
- 如何在win上搭建SVN服务器
本博文转自:http://www.cnblogs.com/armyfai/p/3985660.html SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本, ...
- js选择器
原生JS选择器有getElementById.getElementsByName.getElementsByTagName和getElementsByClassName这四个,下面我就一个一个介绍这四 ...
- Windows 8.1 应用开发文章汇总
Windows 8.1 应用再出发 - C# + XAML 1. Windows 8.1 应用再出发 - 创建我的第一个应用 2. Windows 8.1 应用再出发 - 几种常用控件 3. Win ...
- winAPI 中 的 GlobalLock GlobalUnlock 的作用
在项目中遇到GlobalLock GlobalUnlock 这两个操作内存的函数. 百度百科解释为:锁定内存中指定的内存块,并返回一个地址值,令其指向内存块的起始处.除非用 GlobalUnlock ...
- U盘占空间,但看不到内容——解决方案
原因: u盘感染了病毒 .病毒把U盘里的文件都加上了隐藏属性和系统属性,所以看不到了,但仍占用存储空间. 解决办法: 1.在“运行“里面输入:cmd,回车:2.在cmd中进入U盘.比如你的U盘是H盘, ...
- 积累一点ctf需要掌握的常见脚本知识
1.暴力破解压缩包. 2.利用像素点还原图片. from PIL import Image import re if __name__ == '__main__': x = 887 //将像素点个数进 ...