C++对象的内存模型
来源:互联网 发布:博迅软件 编辑:程序博客网 时间:2024/06/05 23:52
看了C++ under the hood之后,C++对象的内存模型是这样的:涉及到虚的,就需要额外的存储空间。虚这里指的是虚函数和虚继承,额外的存储是对象内部需要存储虚函数表指针和虚基类表指针。
一个类的内存的分布在VC下是这样的:先是非虚基类子对象(包括非虚基类的虚函数表指针和成员),接着是虚基类表指针,接着才是该类本身扩展的成员,然后是虚基类子对象。该类的虚函数如果在基类中出现,会在对应的虚函数表中的位置对基类的进行替代。而不曾在基类中出现的虚函数,会加到最前边的非虚基类子对象的虚函数表的尾部,所以如果没有非虚基类,则在起始位置新建一张虚函数表,用来保存这些多余的虚函数的位置。
一般的,初始化的顺序是按继承的顺序,从上到下,从左到右调用基类的构造函数(虚继承子对象会破坏这个规则,最先调用),然后是成员对象的构造函数,然后是虚函数表和虚基类表指针的初始化,最后是其他成员的初始化。另外,对于虚继承,虚继承子对象如果没有默认构造函数,在每一层的每一个派生类中都要显示的调用其构造函数,此时如果其基类已经调用了虚继承子对象的构造函数,在派生类的构造函数的初始化列表中,仍然要显示的调用,其基类的构造函数中对虚继承子对象的构造函数的调用将忽略。
class A; //A假如没有默认的构造函数class B:virtual public Aclass C:virtual public Aclass D:public B, virtual public C //在D的初始化列表中仍然要显示的调用A的构造函数(B、C构造函数的对A构造函数的调用将忽略)
也就是说,须基类的构造函数,在每一层的派生类的构造函数中都会被调用(A没有默认的构造函数,需要手写调用;有默认的,编译器安插A的构造函数在D的构造中);也就是说,上边D的构造函数中,仍然会调用A的构造函数,而不是将A的非虚继承中,包裹在B中或者C中,那样D只需要调用B的和C的就行;但是虚继承,D的还是会调用A的,而忽略在B和C中对A的调用。
利用这个原理,如果A的构造函数为private,假设B是A的友元,同时,B虚继承于A;那么,B将是一个无法被继承的类;因为如果C继承于B,那么C的构造函数中会去调用A的构造函数,这是不允许的。而且这里B可以正常在栈上构造,而普通的防治被继承的方法实际上使用的是单例的模式。
这就是C++中的黑科技。
这里也回顾一下C++的访问控制:public 是面向公众的,protected 是留给孩子的,private 是对朋友开放的(当然从内存上来看)。需要说明的是,孩子又有三种,当然从内存上来看,无论哪种孩子,派生类对象的内存里都有一个完成的基类子对象。默认情况下,三种孩子对于基类中的public 和 protected,继承过来知道,public 孩子变成自己的public 和 protected, protected 孩子都变成自己的protected,private 孩子都变成自己的private ;当然可以用using 关键字改变这种默认行为。
另外,如果不是为了多态,在派生类和基类中使用相同的函数名是个糟糕的设计;是虚函数而且必须函数签名相同才能是多态。其他情况下,基类的指针(或者引用)指向派生类,永远只能调用基类中定义过的函数,当然是函数名相同,签名不同,仍然是不同的函数。所谁的指针调用谁的函数。派生类的指针将无法调用与派生类中定义的同名的基类中的函数--> 这是个糟糕的设计,当然任何在访问权限内,总是可以用域运算符来访问基类的(所有的访问前都可以加域运算符,只是不简洁)。
为了无心造成的这个设计,C++11推出两个新的关键字:final 和 override;final修饰类是,表示这个类不能被继承。final 在基类中修饰虚函数,表示派生类中不能再重写这个虚函数,否则会出错;override 在派生类中修饰虚函数,表示这个虚函数是对基类的重写,否则会出错。
- 对象的内存模型
- 对象的内存模型
- 类、对象的内存模型
- 对象的内存模型基础知识
- PHP对象的内存模型
- Java对象的内存模型
- C++对象的内存模型
- C++对象的内存模型
- C++对象的内存模型
- Java对象的内存模型
- 【C++】C++对象内存模型简介
- 漫谈C++:对象内存模型分析
- C++-对象继承内存模型配图
- C/C++的内存模型
- C语言的内存模型
- C语言的内存模型
- C/C++的对象模型
- Objective-C的对象模型
- 平台服务器句柄泄露问题的排查与解决
- altium designer 09丝印靠近焊盘显示绿色警告,如何阻止其报警?
- 父类子类的静态初始化块,初始化块,构造器执行顺序
- JavaScript中清空数组的三种方式
- TCP/IP协议三次握手与四次握手流程解析
- C++对象的内存模型
- Android LayoutInflater深度解析 给你带来全新的认识
- xp 删除虚拟网卡
- [leetcode] Swap Nodes in Pairs
- [安卓]手机管家(十七)进程管理NO. 2
- 欢迎使用CSDN-markdown编辑器
- 《统计学习方法》笔记(一)
- 在购大班公众具时必要慎重思量多方面的原则
- TQ210 交叉编译环境搭建