C++对象模型复习
一:对象模型
C++面向对象的实现,相对于C耗费成本是由virutal引起的。包括
- virtual function机制,用来支持执行期绑定。
- virutal base class 虚基类机制,以实现共享虚基类的subobject
此外还有多重继承下,发生在其第二或后继派生类之间的转换。
C++对象模型,所有非静态数据成员存储在对象本身中,所有的静态数据成员,成员函数(包括静态与非静态)都置于对象之外。虚函数的支持是在每个class内部维护一个vptr指向vtbl,vtbl存放所有虚函数指针。vptr的设定和重置都由每一个class的构造函数/析构函数/拷贝分配函数自动完成。每一个typeinfo也放在vtbl之中(用来支持RTTI),vtbl之中还有virtual base subobject的offset值。
主要有三种方法支持多态:(1)基类使用引用或指针接收派生类。(2)可调用虚函数触发多态。(3)经由dynamic_cast和typeid运算符。
当一个基类对象被直接初始化为一个派生类对象时,派生类对象就会被切割,仅剩基类类型大小的内存。
总结:
总而言之,多态是一种威力强大的设计机制,允许通过将派生类的指针赋给基类指针,让基类可以根据赋值给它的子类的特性以不同的方式运作。需要付出的代价就是额外的间接性,包括虚表和RTTI两方面。
二:构造函数语意学
四种情况下编译器自动合成trival构造函数:
1.包含带有默认构造函数的对象成员的类 2. 继承自带有默认构造函数的基类的类 3.带有虚函数的类 4.带有一个虚基类的类
拷贝构造函数同理,否则执行默认位逐次拷贝。
NRV优化不必说。
成员初始化列表必须使用的情况:1.有const成员 2.有引用类型成员 3.有没有默认构造函数的成员对象 4.基类对象没有默认构造函数
三:Data语意学
影响C++类的大小的三个因素:
- 支持特殊功能所带来的额外负担(对各种virtual的支持)
- 编译器对特殊情况的优化处理(空class)
- 内存对齐
一般对象的布局顺序为:vptr、基类成员(按声明顺序)、自身数据成员、虚基类数据成员(按声明顺序)
四:Function语意学
C++支持三种类型成员函数,分别是static、nonstatic、virtual。
非静态成员函数:和非成员函数由相同的效率,因为编译器内部会将其转换为非静态成员函数,加上this指针作为额外参数。此外还要额外对函数的名称进行处理。
虚拟成员函数p->function转换为(*p->vptr[1])(p),引入虚表下表。
静态成员函数实际上相当于全局函数。不能够存取类中非静态成员,不能调用非静态成员函数。不能够声明为const、voliatle或virtual。它不需要经由对象调用,当然,通过对象调用也被允许。
消极多态与积极多态:
Point ptr = new Point3d //Point3d继承自Point
在这种情况下,多态可以在编译期完成(虚基类情况除外),因此被称作消极多态(没有进行虚函数的调用)。相对于消极多态,则有积极多态--指向的对象类型需要在执行期才能决定。积极多态的例子如虚函数和RTTI:
//例1,虚函数的调用
ptr->z();
//例2,RTTI的应用
if(Point3d *p = dynamic_cast<Point3d*>(ptr))
return p->z();
虚基类子对象为什么是运行时确定的,而不是编译器? 因为虚基类的构造函数在继承体系中被编译器压制到了最派生类才构造,所以集成体系中间的类不能再编译器确定虚基类子对象的位置。因为它下面可能还有多层继承。所以要引入一个间接性,为虚继承体系中每个class引入一个虚基类子对象offset,在运行时填充该offset。
在多重继承中支持虚函数,其复杂度围绕在第二个或后继基类身上,以及“必须调整this指针这一点”。多重继承中调整this指针(注意没说虚函数):比如对于base *pbase = new derived(这里没有多态),新的derived对象以指向base subobject。如果没有这样的调整,指针的任何非多态运用都将失败,向base->data_base,存取操作会失败!或者delete base,这个操作执行时又必须把this指针指到头部,因为要析构的大小是derived对象大小。这些操作必须在执行期完成,也就是this指针跳跃的这些offset必须指到,其实会存于vtbl之中。如果base是派生类的最基类,那么this无需调整,因为他们起始位置是一样的。
有三种情况,第二或后继的基类会影响对虚函数的支持:
- 通过一个指向第二基类的指针,调用派生类虚函数(指针必须后移)。
- 通过一个派生类的指针,调用第二个基类中一个继承而来的virtual function,此情形派生类指针必须再次调整,以指向第二个基类子对象。
- 第三种情况发生于一个语言扩充性质之下,允许一个虚函数返回值类型发生变化,可能是基类类型,或者派生类类型。比如base* pb=pb->clone(),该函数返回值为derived*类型,所以此处必须调整offset。
五
六
七
1.为什么catch字句的异常声明通常被声明为引用?
可以避免由异常对象到catch字句中的对象的拷贝,特别是对象比较大时。
能确保catch字句对异常对象的修改能再次抛出
能确保正确地调用与异常类型相关联的虚拟函数,避免对象切割
异常对象的声明周期?
产生:throw className()时产生
销毁:该异常最后一个catch字句退出时销毁
注意:因为一场可能在catch子句中重新被抛出,所以在到达最后一个处理该异常的catch子句之前,异常对象是不能销毁的。
RTTI:
- RTTI只支持多态类,也就是说没有定义虚函数的类是不能进行RTTI的。
- 对指针的dynamic_cast失败时会返回NULL,对引用的话,失败会抛出bad_cast_exception.
- typeid可以放回const type_info&,用以获取类型信息。
关于1是因为RTTI的实现时通过vptr来获取存储在虚函数表中的type_info*,事实上为非多态类提供RTTI,也没有多大意义。2的原因在于指针可以被赋值为0,以表示no object,但是引用不行。关于3,UI然第一点指出RTTI只支持多肽类,但typeid和typeinfo同样可适用于内建类型及所有非多态类。与多态类的差别在于,非多态类的type_info对象是静态取得,**所以不能叫“执行期类型识”。
dynamic_cast主要用于类层次之间的上行装换和下行转换,还可以用于类之间的交叉转换。上行转换时,static_cast是安全的。在进行下行转换时,dynamic_cast有类型检查的功能,比static_cast更安全:
#include <iostream>
#include <assert.h>
class base {
public:
virtual void fun() { std::cout<<"base"<<std::endl; } //去掉virtual dynamic_cast会编译失败
};
class derived : public base {
public:
char* str[100];
};
void test(base* b)
{
derived* d1 = static_cast<derived*>(b);
//d1->str[1] = "hello"; //使用static_cast不会类型检查,如果b指针真得是derived*类型此处就没问题,如果b实际上就是base类型,此处段错误
derived* d2 = dynamic_cast<derived*>(b);
assert(d2 == NULL); //使用dynamic_cast,如果b只是base*类型,上一句强转称为derived*类型会失败,d2会返回NULL。
}
int main()
{
base *b;
test(b);
return 0;
}
另外,dynamic_cast还支持交叉转化(cross cast),也就是继承体系中统一层级的对象之间的转换。而使用static_cast这种无关连的类的转换会编译不通过。
#include <iostream>
class A
{
public:
int m_iNum;
virtual void f(){}
};
class B:public A
{
};
class D:public A
{
};
void foo()
{
B* pb = new B;
pb->m_iNum = 100;
//D* pd1 = static_cast<D*>(pb); //compile error
//D* pd2 = dynamic_cast<D*>(pb);//pd2isNULL
delete pb;
}
int main()
{
foo();
}
C++对象模型复习的更多相关文章
- C++ 系列:C++ 对象模型
1 何为C++对象模型 C++对象模型可以概括为以下2部分: 1.语言中直接支持面向对象程序设计的部分: 2.对于各种支持的底层实现机制 语言中直接支持面向对象程序设计的部分,如构造函数.析 ...
- JS复习--更新结束
js复习-01---03 一 JS简介 1,文档对象模型 2,浏览器对象模型 二 在HTML中使用JS 1,在html中使用<script></script>标签 2,引入外部 ...
- [Java面试一]面试复习大纲.
一.Java基础部分 (搞定所有技术之后才考虑复习的技术点) 1.数组中的排序问题(笔试或者机试,前者可能性更大) 2.面向对象的理解 3.集合相关的问题,比如hashmap跟hashtable的区别 ...
- 复习面向对象的OOA、OOD、OOP
复习 OOA.OOD.OOP OOA Object-Oriented Analysis:面向对象分析方法 是在一个系统的开发过程中进行了系统业务调查以后,依照面向对象的思想来分析问题. OOA与结构化 ...
- java复习(1)---java与C++区别
[系列说明]java复习系列适宜有过java学习或C++基础或了解java初步知识的人阅读,目的是为了帮助学习过java但是好久没用已经遗忘了的童鞋快速捡起来.或者教给想快速学习java的童鞋如何应用 ...
- Javaweb学习笔记——(十五)—————— sql复习
sql复习 数据库管理系统(DBMS)的概述 1.什么是DBMS:数据的仓库 *方便查询 *可存储的数据量大 *保证数据的完整.一致 *安全可靠 2.DBMS的发展:今天主流数据库为关系型数据库管理系 ...
- HIBERNATE知识复习记录3-关联关系
先上一张图,关于几种关系映射: 抄一段解释: 基本映射是对一个实体进行映射,关联映射就是处理多个实体之间的关系,将关联关系映射到数据库中,所谓的关联关系在对象模型中有一个或多个引用.关联关系分为上述七 ...
- Hibernate复习之Hibernate基本介绍
众所周知.眼下流行的面向对象的对象关系映射的Java持久层框架有MyBatis和Hibernate.他们都是对象关系映射ORM. 解决的主要问题就是对象-关系的映射.域模型和关系模型都分别建立在概念模 ...
- JS---part5 课程介绍 & part4 复习
part5 课程介绍 另一个定时器 第一个定时器的小案例----练习 封装动画函数----------匀速的动画函数,过渡到=======>缓动的动画函数 简单的轮播图 左右焦点的轮播图 无缝连 ...
随机推荐
- npm install
npm install moduleName 命令 1. 安装模块到项目node_modules目录下.2. 不会将模块依赖写入devDependencies或dependencies 节点.3. 运 ...
- Ubuntu18.04安装SS(不是服务器端!!!)
终于下定决心把我1T的机械硬盘格式化了- -,分了100G装了Ubuntu 18.04,在安装shadowsocks的时候有些东西想记下来.shadowsocks目前在ubuntu上使用的主要有两个版 ...
- LINQ更新提示找不到行或行已更改的解决一例
LINQ对一行数据进行更改,怎么都无法提交,所有字段值都不是空值,后来看了实体,我发现更改的数据是主键,去数据库看这个字段却不是主键,原来是数据库取消主键了,实体代码没取消,因为更改了主键,所以无法更 ...
- 每天一套题打卡|河南省第十一届ACM/ICPC
A 计划日 题意:已知李明在YYYY年MM月DD日星期W订了学习计划,现在想看看李明N天后的完成情况和个人总结,你能告诉我那天的日期和星期几吗? 模拟日期计算: 计算星期可以用基姆拉尔森公式 //中国 ...
- mui中confirm在苹果出现bug,confirm点击确定跳转页面再返回后,页面被遮罩盖住无法使用
项目中使用confirm mui.confirm('您还未抽奖,现在去抽奖吗?', function (res) { if (res.index === 1) { window.location.hr ...
- Deep Learning--week1~week3
week1 一张图片,设像素为64*64, 颜色通道为红蓝绿三通道,则对应3个64*64实数矩阵 为了用向量表示这些矩阵,将这些矩阵的像素值展开为一个向量x作为算法的输入 从红色到绿色再到蓝色,依次按 ...
- 调用Bytom Chrome插件钱包开发Dapp
安装使用插件钱包 1. 打开Google浏览器的应用商店,搜索Bystore 下载链接:http://t.cn/E6cFFwb 2. 然后点击添加到Chrome,就可以添加到我们的: 3. 使用goo ...
- Java问题解决:springboot启动出现-Your ApplicationContext is unlikely to start due to a @ComponentScan of the default package
参考资料: http://www.mamicode.com/info-detail-2101273.html https://blog.csdn.net/u012834750/article/deta ...
- 2>&1的意思
2>&1的意思就是将标准错误也输出到标准输出当中.
- wordpress-基础插件,常用函数
一,插件制作 1.首先在plugin文件夹下创建一个php文件,我以制作一个banner插件为例,把以下代码拷贝到php文件中 <?php add_action("init" ...