《深入理解C++对象模型》第1章笔记

加上封装后的布局成本
member functions虽然含在class声明之内,却不出现在object之中,每一个non_inline member function只会诞生一个函数实体。至于每一个“拥有零个或一个定义”的inline function则会在其每一个使用者身上产生一个函数实体。这一个并未带给它任何空间或执行期的不良回应。C++在布局以及存取时间上主要的额外负担是由vitual引起,包括:virtual function机制、vitrtual base class。

C++对象模型
  1. 简单对象模型
    在这里插入图片描述

members本身并不放在object之中。只有“指向member的指针”采放在object内。这么做可以避免“member有不同的类型,因而需要不同的存储空间”所招致的问题。但是这个模型并没有并运用与实际产品上,不过关于索引或solt数目的观念,倒是被运用到C++的“指向成员的指针”(Pointer-to-member)观念之中。

  1. 表格驱动对象模型
    在这里插入图片描述
  2. C++对象模型
    在这里插入图片描述

可以说C++对象模型是继承了简单对象模型和表格驱动模型的两者的优点。它是从简单对象模型派生而来的,并对内存空间和存取时间做了优化。在模型中,Nonstatic data member被配置于每一个class object之内,static data member则被存放在所有的class object之外,static和Nonstatic function members也被放在所有的class object之外。Visual functions则以两个步骤支持之:
(1)每一个class产生一堆指向visual functions的指针,放在表格之中。这个表格称为visual table(vtbl)。(这种思想是从表格驱动模型中继承而来的)。
(2)每一个class object被添加了一个指针,指向相关的visual table(虚函数表)。通常这个指针称为vptr。vptr的设定和充值均由每一个class的constructor,destructor和copy assignment运算符自动完成。每一个class所关联的type_info object也经由visual table指出来,通常放在表格的第一个solt处。

加上继承,对象模型的影响
  1. “简单对象模型”中,每一个base class可以被derived class object内的每一个solt指出,该solt内含base class object的地址。这种机制的主要缺点:间接性导致空间和存取时间上的额外负担,优点则是class object的大小不会因其base class的改变而受影响。
  2. “base table模型”也就是表格驱动模型中,base class table被产生出来时,表格中的每一个solt内含一个相关的base class地址。主要缺点:由于间接性而导致空间和存取时间上的额外负担,优点则是在每一个class object中对于继承都有一致的表现方式:每一个class object都应该在某个固定的位置上安放一个base table指针,与base classes的大小或数目无关,第二个优点,不需要改变class object本身,就可以放大、缩小、或更改base class table。
关键字差异struct和class
  1. struct关键词的使用实现的是C的数据集合体,而class关键词实现的是C++的ADT观念。C++原本不是必须需要class关键字,但是它的加入,体现了C++的封装和继承的哲学。
  2. struct是按声明顺序来存放数据,故可以使用小技巧来实现一个可变大小的数组,但是C++中class内数据存储却不一定是与声明一致,如:base classes和derived classes的data members的布局并没有强制规定谁先谁后。
  3. struct默认public,class默认private
对象的差异

C++程序设计模型支持三种programming paradigms(程序设计范式):

  • 程序模型:面向过程
  • 抽象数据类型模型ADT:该模型所谓的“抽象”是和一组表达式(public接口)一起提供,而其运算定义隐而未明
  • 面向对象模型OO:类和继承、多态等,只有通过pointer和reference的间接处理才支持多态性质。

其中有以下方法支持堕胎:

  1. 把一个derived class指针转换为一个指向public base type的指针
  2. virtual function机制
  3. dynamic_cast<>转换指针类型
    多态的主要用途是经由一个共同接口来影响类型的封装,接口以virtual function机制引发,可以在执行期(dynamic)根据object的真正类型解析出到底哪一个函数实例被调用
class object的大小:

一般而言要有:

  • nonstatic data members大小
  • 加上由于译注的需求而填补上去的空间(一般将数值调整到某数的倍数)
  • 加上支持virtual而产生的额外负担
class A
{
    
    
	public:
		A();
		virtual ~A();
	protected:
		int loc;
		string name;
		
};
int main()
{
    
    
	cout<<sizeof(int)<<" "<<sizeof(string)<<endl;
	cout<<sizeof(A);
}

在这里插入图片描述
所以64位计算机上alignment为8bytes(既大小为8的倍数)

指针类型

指向不同类型的指针的差异在于其所寻址出来的object不同,指针类型教导编译器如何解释某个特定地址中内存内容以及大小。转换指针类型,只影响”被指出之内存的大小和其内容“的解释方式。

加上多态
class Zoo
{
    
    
	...
}

class Bear:public Zoo
{
    
    
	...
}

Bear b;
Zoo* pz = &b;
Bear* pb = &b;

假如Zoo大小为16bytes(不算是译注),Bear大小为24bytes,那在内存空间中,地址1000-1024为Bear,其中1000-1016为Zoo的,pb指针涵盖整个Bear object,和pz指针之涵盖Bear object中的Zoo subobject。除了Zoo subobject中的members,不能用pz来处理其他Bear 的members,除了通过virtual机制。

pz->dance() //不合法

(static_cast<Bear*>(pz))->dance(); //合法

if(Bear* pb2 = dynamic_cast<Bear*>(pz))
	pb2 -> dance() 合法但是run time

类型的信息封装并不是维护于pz之中,而是维护于link中,此link存在于object的vptr和virtual table之间。

Bear b;
Zoo a = b;

这样子会切割b中的Zoo subobject部分以填入Zoo object比较小的内存之中。不会复制b的虚函数表是因为编译器必须确保如果某个object含一个或以上的vptrs,vptrs的内容不会被base class object初始化或改变,所以不会复制。否则就可能会被改变。

小结

C++哲学思想:面向对象,其中:封装、继承、多态是核心,为了实现封装引入class关键字,然后详细介绍了class和struct的区别,还介绍了C++对象模型,C++的多态机制,内存分配等。

猜你喜欢

转载自blog.csdn.net/ZmJ6666/article/details/108937772