一,简单继承:

#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++面向对象 之 继承、多态的更多相关文章

  1. php面向对象 封装继承多态 接口、重载、抽象类、最终类总结

    1.面向对象 封装继承多态  接口.重载.抽象类.最终类 面向对象 封装继承多态  首先,在解释面向对象之前先解释下什么是面向对象? [面向对象]1.什么是类? 具有相同属性(特征)和方法(行为)的一 ...

  2. python面向对象(封装,继承,多态)

    python面向对象(封装,继承,多态) 学习完本篇,你将会深入掌握 如何封装一个优雅的借口 python是如何实现继承 python的多态 封装 含义: 1.把对象的属性和方法结合成一个独立的单位, ...

  3. Java基础——面向对象(封装——继承——多态 )

    对象 对象: 是类的实例(实现世界中 真 实存在的一切事物 可以称为对象) 类: 类是对象的抽象描述 步骤: 1.定义一个类 (用于 描述人:) ( * 人:有特征和行为) 2.根据类 创建对象 -- ...

  4. python面向对象之继承/多态/封装

    老师说,按继承/多态/封装这个顺序来讲. 子类使用父类的方法: #!/usr/bin/env python # coding:utf-8 class Vehicle: def __init__(sel ...

  5. python基础语法15 面向对象2 继承,多态,继承json模块中JSONEncoder,并派生出新的功能

    继承 1.什么是继承? 继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类. - 在Python中,一个子类可以继承多个父类.(面试可能会问) - 在其它语言中,一个子类只 ...

  6. java面向对象(封装-继承-多态)

    框架图 理解面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是一种思想 面向过程强调的是功能行为 面向对象将功能封装进对象,强调具备了功能的对象. 面向对象是基于面向过程的. 面向对象的特点 ...

  7. -1-2 java 面向对象基本概念 封装继承多态 变量 this super static 静态变量 匿名对象 值传递 初始化过程 代码块 final关键字 抽象类 接口 区别 多态 包 访问权限 内部类 匿名内部类 == 与 equal

    java是纯粹的面向对象的语言 也就是万事万物皆是对象 程序是对象的集合,他们通过发送消息来相互通信 每个对象都有自己的由其他的对象所构建的存储,也就是对象可以包含对象 每个对象都有它的类型  也就是 ...

  8. day25 面向对象继承,多态,

    这两天所学的都是面向对象,后面还有几天也是它,面向对象主要有三个大的模块,封装,继承,多态,(组合),昨天主要讲了面向对象的命名空间,还有组合的用法,今天是讲的继承还有继承里面所包括的钻石继承,以及多 ...

  9. Python 面向对象编程 继承 和多态

    Python 面向对象编程 继承 和多态 一:多继承性 对于java我们熟悉的是一个类只能继承一个父类:但是对于C++ 一个子类可以有多个父亲,同样对于 Python一个类也可以有多个父亲 格式: c ...

随机推荐

  1. Xilinx的约束文件

    FPGA中有三种约束文件,分别是用户设计文件(.ucf文件),网表约束文件(.NCF文件)与物理约束文件(.PCF文件). 在设计阶段,需要硬件描述文件与UCF文件,经过综合后生成NCF文件,最后得到 ...

  2. .net走向设计2—设计工具

    1.思维导图 2.项目管理工具 3.常用UML工具 4.数据库设计工具

  3. centos 如何清理/dev/vda1系统盘

    df-h检查一台服务器磁盘使用空间,发现磁盘已经使用了100% 思路是: 1.cd /usr 2.du -sh * 看哪个目录占用空间大 3.重复前两步,根据实际情况删除或者移走 4.日志的话可以运行 ...

  4. ASP.NET Web API 2 入门

    本文参考:http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web ...

  5. ActionScript 3.0 自写类整理笔记(十三)——Random类

    一个简单的随机函数工具类,总共提供了9种静态方法来获取不同的随机值随便写的,如果你还有什么更好的建议,请提出来,谢谢~ index.Random类:代码:public final class Rand ...

  6. CSS无序列实现表宽度自适应的表格

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. 易学PHP——WAMP环境搭建

    安装前的准备 查看本机的类型(x86/x64,即 32 位还是 64 位) 安装好 VC9/VC11/VC14 运行库(需要按照本机的类型选择安装) 创建好一个安装路径用于安装整套 amp 环境(我使 ...

  8. Ubuntu 16.04 启动错误 "a start job is running for hold until boot process finishes up"

    老司机也差点翻船... 升级16.04的时候,将默认启动管理器(default display manager)选为gm3(gnome3)了(应该使用默认的lightgm)如果改成gm3,好像是nvi ...

  9. Springmvc中@RequestParam传值中文乱码解决方案

    首先jsp表单里面有一些参数要传到controller中,通过以下方法接收: @RequestMapping(value="/orderPaper") public ModelAn ...

  10. mysql下载安装

    1.下载 下载地址:http://dev.mysql.com/downloads/mysql/ 根据电脑配置来选,我选了 windows(x86,64-bit),ZIP Archive这个,点击Dow ...