C++面向对象编程之对象模型(objectmodel)关于vptr和vtbl的讲解
1. 对象模型(objectmodel):关于vptr和vtbl
注意:
1)C++编译器一旦发现类中有虚函数,就会为该类生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针(即从内存角度上看,含有虚函数的对象里头会多一个指针)。在32为机器上,一个指针占4字节的内存空间,所以sizeof值为4,。在64位机器上,一个指针占8字节内存,所以sizeof值为8。
2)虚函数表放的是指针(地址)。虚指针关联虚函数,和一般函数无关。
3)C是以静态绑定的方式调用函数的。而C++是以动态绑定的方式调用函数的,C++调用虚函数若以C的风格来写,应该为:
(*(p->vptr)[n])(p); // 或 (* p->vptr[n])(p);4)继承的是函数调用权,父类有虚函数,则子类一定有虚函数。例子:
class A{ public: virtual void vfunc1(); virtual void vfunc2(); void func1(); void func(); private: int m_data1, m_data2; }; class B:public A { public: virtual void vfunc1(); void func2(); private: int m_data3; }; class C:public B{ public: virtual void vfunc1(); void func2(); private: int m_data1, m_data4; };
5)前面提到C++绑定函数有两个做法,一个是静态绑定call。一个是动态绑定,动态绑定的三个条件是:通过指针调用;向上转型;调用的是虚函数。
6)声明一个容器,里面放指针,指向父类(声明为指向父类的指针,但绑定的(new)是子类)。当子类都有修改某个虚函数,通过遍历容器对象,调用这个虚函数,就能依次调用各自的版本,如下图。(再C里面实现这个行为靠if else else …)。
虚函数的这种做法就是“多态”,同样是point toA,但是却实际上指向不同的东西。下面的例子可以解释这个行为(注意这里的this pointer。简单的说,通过对象来调用一个函数,那个对象的地址就是this pointer。所有成员函数都有隐藏参数this):
7)关于动态绑定(Dynamic Binding):
(*(p->vptr)[n])(p); // 或 (* p->vptr[n])(p);
意思是:通过指针找到它的虚指针,再找到它的虚函数表,取出第n个,把它当成函数指针去调用。由于是通过p去调用,因而p是this pointer。(这句话也解释了上面的多态行为)
2.谈谈const
const member functions (常量成员函数)
当对象调用成员函数的时候,对象可能是const也可能不是const,成员函数可能是const也可能不是const,这样就存在两种情况。
规则:当成员函数的const和non-const版本同时存在,const object只会(只能)调用const版本,non-const object只会(只能)调用non-const版本。(而当只有一者存在时,const object也只能调用const的版本。但是non-const object却可能调用const 或non-const 版本。)
下面这个例子是设计的比较不好的:
const String str("hello world"); str.print(); // 如果当初设计 Srting::print()时未指明 const, // 那么就是由 const object调用 non-const member function,会出错。
3.关于new,deete
0)在C++中,设计size_t就是为了适应多个平台的。size_t的引入增强了程序在不同平台上的可移植性。
1)new和delete是表达式,可以分解进去(编译器转化)。
2)重载::operator new, ::operatordelete, ::operator new[], ::operator delete[]
即重载全局函数。没有重载就用全局的。注意delete要接受指针。
3)重载member operator new/delete
接管了内存分配要做什么?做一个内存池。
4)重载member operator new[]/delete[]
接管使用者的行为
示例,接口。。。。。。
如果使用者想绕过设计者的设计,可以加全局的作用域。(写上global scope operator :: 会绕过前述所有overloaded functions,强迫使用global version)
5)重载new(), delete()
placement new:
我们可以重载class member operatornew(), 写出多个版本,前提是每一版本的声明都必须有独特的参数列,其中第一参数必须是size_t, 其余参数以new所指定的placemnet arguments 为初值。出现于new (……) 小括号内的便是所谓的placement arguments。
我们也可以重载class member operatordelete() (或称此为placement operator delete),写出多个版本。但他们绝不会被delete调用。只有当new所调用的ctor抛出exception,才会调用这些重载版的operatordelete()。它只可能这样被调用,主要用来归还未能完全创建成功的object所占用的memory。
这些对应的delete被调用的时机只有一个,那就是对应的new分配内存之后,如果构造函数发挥异常(表示构造对象时失败),那就把刚刚分配的内存释放掉。
6)basic_string 使用new(extra)扩充申请量
(这部分不太理解,后面补上。)
上一篇: C语言之简单物理学问题实现
下一篇: 朋友圈三天可见,透露了你的社交观