一:对象模型

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无需调整,因为他们起始位置是一样的。

有三种情况,第二或后继的基类会影响对虚函数的支持:

  1. 通过一个指向第二基类的指针,调用派生类虚函数(指针必须后移)。
  2. 通过一个派生类的指针,调用第二个基类中一个继承而来的virtual function,此情形派生类指针必须再次调整,以指向第二个基类子对象。
  3. 第三种情况发生于一个语言扩充性质之下,允许一个虚函数返回值类型发生变化,可能是基类类型,或者派生类类型。比如base* pb=pb->clone(),该函数返回值为derived*类型,所以此处必须调整offset。

1.为什么catch字句的异常声明通常被声明为引用?

  1. 可以避免由异常对象到catch字句中的对象的拷贝,特别是对象比较大时。

  2. 能确保catch字句对异常对象的修改能再次抛出

  3. 能确保正确地调用与异常类型相关联的虚拟函数,避免对象切割

  4. 异常对象的声明周期?

  5. 产生:throw className()时产生

  6. 销毁:该异常最后一个catch字句退出时销毁

  7. 注意:因为一场可能在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++对象模型复习的更多相关文章

  1. C++ 系列:C++ 对象模型

    1      何为C++对象模型 C++对象模型可以概括为以下2部分: 1.语言中直接支持面向对象程序设计的部分: 2.对于各种支持的底层实现机制 语言中直接支持面向对象程序设计的部分,如构造函数.析 ...

  2. JS复习--更新结束

    js复习-01---03 一 JS简介 1,文档对象模型 2,浏览器对象模型 二 在HTML中使用JS 1,在html中使用<script></script>标签 2,引入外部 ...

  3. [Java面试一]面试复习大纲.

    一.Java基础部分 (搞定所有技术之后才考虑复习的技术点) 1.数组中的排序问题(笔试或者机试,前者可能性更大) 2.面向对象的理解 3.集合相关的问题,比如hashmap跟hashtable的区别 ...

  4. 复习面向对象的OOA、OOD、OOP

    复习 OOA.OOD.OOP OOA Object-Oriented Analysis:面向对象分析方法 是在一个系统的开发过程中进行了系统业务调查以后,依照面向对象的思想来分析问题. OOA与结构化 ...

  5. java复习(1)---java与C++区别

    [系列说明]java复习系列适宜有过java学习或C++基础或了解java初步知识的人阅读,目的是为了帮助学习过java但是好久没用已经遗忘了的童鞋快速捡起来.或者教给想快速学习java的童鞋如何应用 ...

  6. Javaweb学习笔记——(十五)—————— sql复习

    sql复习 数据库管理系统(DBMS)的概述 1.什么是DBMS:数据的仓库 *方便查询 *可存储的数据量大 *保证数据的完整.一致 *安全可靠 2.DBMS的发展:今天主流数据库为关系型数据库管理系 ...

  7. HIBERNATE知识复习记录3-关联关系

    先上一张图,关于几种关系映射: 抄一段解释: 基本映射是对一个实体进行映射,关联映射就是处理多个实体之间的关系,将关联关系映射到数据库中,所谓的关联关系在对象模型中有一个或多个引用.关联关系分为上述七 ...

  8. Hibernate复习之Hibernate基本介绍

    众所周知.眼下流行的面向对象的对象关系映射的Java持久层框架有MyBatis和Hibernate.他们都是对象关系映射ORM. 解决的主要问题就是对象-关系的映射.域模型和关系模型都分别建立在概念模 ...

  9. JS---part5 课程介绍 & part4 复习

    part5 课程介绍 另一个定时器 第一个定时器的小案例----练习 封装动画函数----------匀速的动画函数,过渡到=======>缓动的动画函数 简单的轮播图 左右焦点的轮播图 无缝连 ...

随机推荐

  1. wireshark基础学习—第四部分wireshark过滤器总结

    这两天一直在熟悉wireshark的过滤器语法规则,以前也接触过这个工具,但只是学校老师教的如何去选择一个接口进行抓取,以及如何去分析一个包的数据.可惜当时对此也没有过多深入.对于我当前,并未接触太多 ...

  2. ios高级开发之多线程(二)NSThread技术

    多线程技术是有多套解决方案的,那么我们该如何选择呢? 技术方案 简介 语言 线程生命周期 使用频率 pthread 1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨 ...

  3. aop(权限控制)

    创建sysContext (管理请求) package com.tp.soft.common.util; import javax.servlet.http.HttpServletRequest; i ...

  4. Ubuntu 18.04.1安装IntelliJ IDEA

    1.下载安装包 下载地址:https://www.jetbrains.com/idea/download/#section=linux 2.下载完成后,解压到/opt目录 .tar.gz -C /op ...

  5. 安装PyCharm开发工具

    1.进入PyCharm官网 http://www.jetbrains.com/pycharm/ 2.点击现在下载 3.选择windows版本 4.打开安装程序 5.下一步,选择安装路径,安装 6.安装 ...

  6. Jenkins 改成中文语言显示

    到系统管理    插件管理中下载如下插件接口  Localization: Chinese (Simplified) 搜索的时候用ctrl+f 进行搜索,不要用Jenkins下面下的filter 只有 ...

  7. C(n,m)排列组合算法

    主要解决C(n,m)问题 static class Extension { public static IList<IList<T>> GetGroup<T>(th ...

  8. 事务,mybatis

    数据库事务:一件完整的事情, 要么全部成功,要么就全部失败 金典案例:转账 A给B转账:100 A:-100 B:+100 如何开启事务: Start transaction; 之前的转账操作(如果在 ...

  9. Arthur and Questions CodeForces - 518E (贪心模拟)

    大意: 给定序列$a$, 某些位置为'?', 求给'?'赋值使得序列$(a_1+a_2+...+a_k,a_2+a_3+...+a_{k+1},...)严格递增, 且$\sum|a_i|$最小. 化简 ...

  10. Maven中基于POM.xml的Profile来动态切换配置信息

    [转载:https://blog.csdn.net/blueheart20/article/details/52838093] 1. Maven中的profile设置 Maven是目前主流的项目代码结 ...