4.2 虚拟成员函数
来源:互联网 发布:bad air sponge 知乎 编辑:程序博客网 时间:2024/06/05 15:25
Q1:C++多态形式:静态多态性、动态多态性
• 多态:以一个 “public base class” 的指针寻址出一个 “derived class object”(深入探索C++对象模型定义)
• 静态多态性通常称为编译时多态,到底模板是不是多态???我个人认为不是
• 动态多态性通常称为运行时多态,通过虚函数来实现
• 动态多态性的两个条件:
○ 在基类中必须使用虚函数或纯虚函数○ 调用函数时使用基类的指针或引用
Q2:消极多态与积极多态(深入探索C++对象模型中定义)
• 消极多态:指针的多态机能主要扮演一个输送机制的角色,经过这个指针,我们可以在程序的任何地方采用一组派生类类型。这样的传输机制的多态形式称为消极多态
Eg:
point2d p2d; point * ptr = &p2d; //此时,ptr带来的多态性体现为传输机制,通过基类指针传输派生类对象
• 积极多态:当指针所指对象被真正使用时,多态也就变为积极的了
Eg:
ptr->f(); //ptr 所指对象被真正使用
Q3:对虚函数调用的深入探究
• 对如下调用,若z()是一个虚函数,那需要那些信息才能在执行期调用正确的z()实例:
ptr->z()
ptr所指对象的真实类型。(用以选择正确的z()实例)
z()实例的位置
因此,需要在每一个多态的类对象上增加两个成员:
1. 一个字符串或数字,表示 class 的类型;(vptr所指表格的第一项即为类型说明,用以支持RTTI)2. 一个指针,指向某个表格,表格中持有程序的虚函数的执行期地址(vptr)
• 虚函数表的构建与存取皆有编译器掌控,不需要任何执行期的介入
Q4:单一继承下的虚函数
• 在单一继承情况下,一个类对象中,可能包含多个vptr,但只会有一个 virtual table。每一个 table 中内含其对应类对象中所有 active virtual functions 函数实例的地址,这些函数包括:
• 这一个类所定义的函数实例,包括改写的继承自 base class 的函数实例• 继承自 base class 的函数实例,指该类并不会改写的继承实例• 一个 pure_virtual_called()的实例。既可以扮演纯虚函数的空间保卫 角色,又可以当作执行期异常处理函数
• 每一个虚函数都被指派一个固定的索引,这个索引在整个继承体系中保持与特定的虚函数的关系。
Eg:
class Point { public: virtual ~Point(); virtual Point& mult(float) = 0; virtual float y()const{ return 0; } virtual float z()const { return 0; } protected: float _x; }; class Point2d : public Point { public: virtual ~Point2d(); Point2d& mult(float); float y() const { return _y; } protected: float _y; };
其虚函数表中的取值情况如下所示:
Point 的虚函数表如下:
Point2d 的虚函数表如下:
Q5:多重继承下的虚函数
• 多重继承中支持虚函数的两个难点在于:
1. 对第二个以及后继的 base classes 的虚函数的处理2. 必须在执行期调整 this 指针
*备注:对于虚函数改写(覆盖)而言,允许一个虚函数的返回值类型有所变化,可能是 base type,也可能是 publicly derived type(仅限指向类类型的指针与引用)。但函数名称,参数列必须与被改写的函数完全相同
附加知识点:定义一个基类指针指向一个派生类对象,通过该指针调用改写函数时,有以下两种情况:
1. 若改写函数不是虚函数,则调用时调用的为基类的函数实例2. 若改写的函数是虚函数,则调用时调用的为派生类的函数实例
*备注:非虚函数并不在类实例中,不影响类实例的大小。而虚函数则影响类实例的大小,因为会造成附加的 vptr 指针出现在类实例中
• 对多重继承的讨论以如下例子来进行:
class Base1 { public: virtual ~Base1(); virtual void speakClearly(); virtual Base1 * clone()const; }; class Base2 { public: virtual ~Base2(); virtual void mumble(); virtual Base2 * clone()const; void test(); }; class Derived : public Base1, public Base2 { public: virtual ~Derived(); virtual Derived * clone()const; };
此问题中派生类 Derived 对虚函数的支持的困难度体现 Base2 子对象上。即有以下三个问题需要解决:
1. 虚析构函数2. 被继承下来的 Base2::mumble();3. 一组clone()函数的实例
• 对于第一个问题:虚析构函数的分析:
• Eg1:
Base2 * pbase2 = new Derived;
该操作需要对新的 Derived 对象的地址进行调整以指向其 Base2 子对象,即编译时产生如下代码:
Derived * temp = new Derived;Base2 * pbase2 = temp ? temp + sizeof( Base1 ) : 0; //事实上此处的加法操作并不能够在编译期直接设定,因为pbase2所指向的真正对象类型在编译期未知
即将 pbase2 的值调整为该 Derived 对象的 Base2 子对象的位置,否则,任何非多态的运用都会失败。如:
pbase2->test(this); //因为 this 指针不指向 Base2 子对象
• Eg2:
delete pbase2;
对于这种情况,指针需要被再一次调整,使其指向 Derived 对象的起始处,调用正确的虚析构函数实例,然后施行 delete 运算符
• 对于调整 this 指针时不知道偏移的两种处理方法:
将 virtual table 扩大的方法
此时,使得每一个 虚函数的表格中放置的不再是一个指针,而是一个集合,其中内含可能的 offset 值以及函数地址。则此时虚函数的调用变化如下:
(*pbase2->vptr[1])(pbase2); (*pbase2->vptr[1].faddr)(pbase2 + pbase2->vptr[1].offset);
这种情况下,连坐处罚了所有的虚函数调用操作,不管其是否需要 offset 调整
thunk方法
所谓的 thunk 方法是一小段代码,用来以适当的 offset 调整 this 指针, 并跳转到虚函数处。此时,虚函数表中存放的仍然是指针
• 若不需要调整this指针的虚函数,此时 slot 中存放的就是虚函数的地址
• 若需要调整 this 指针的虚函数,此时 slot 中存放的是一个相关的 thunk 的地址
• 对于第二个问题:被继承下来的 Base2::mumble():
• Eg1:
Derived * pder = new Derived;pder->mumble();
对于这种情况,Derived 的指针必须再次调整以指向第二个基类子对象,用以调用该子对象的 mumble() 函数
• 对于第三个问题:clone()函数的实例
• Eg:
Base2 * pb1 = new Derived; Base2 * pb2 = pb1->clone();
在这个过程中,pb1->clone()执行时,pb1会被调整到指向 Derived 对象的起始地址,调用 Derived::clone(),传回一个指向新的 Derived 对象的指针,该对象地址再次进行调整后指向 Base2 子对象,并传送给 pb2
• 如果虚函数够小(平均大小是8行),则将使用 sun 编译器提供的 “split functions” 的技术: 以相同算法产生出两个函数,其中第二个在返回之前,为指针加上必要的 offset
*此时,通过 Derived 指针或 Base1 指针调用函数都不需要调整返回值,而通过 Base2 指针所调用的,则是另一个调整返回值的函数
• 在多重继承中,一个派生类内含 n-1 个额外的虚函数表,其中 n 表示其上一层的基类的个数。(因此,单一继承的派生类只有一个虚函数表)
Q6:虚拟继承下的虚函数
• 虚基类不同于普通基类,虚基类位于实例对象的底部(即实例对象地址的最高处),因此,在此情况下,虚基类与派生类之间的转换必须要调整 this 指针
Eg:
class A{};class B : public virtual A{};
此时虽然类 B 仅有一个基类 A,但由于基类 A 是虚基类,在这种情况下,虚基类 A 位于类 B 的底部,因此,在类 A 与类 B 之间的转换需要调整 this 指针
• 更复杂的情况不予讨论。建议:不要在一个虚基类中声明非静态数据成员
- 4.2 虚拟成员函数
- c++虚拟成员函数
- 4.2 Virtual Member Functions(虚拟成员函数)
- 4.1 成员函数的各种调用方式(静态成员函数,非静态成员函数,虚拟成员函数)
- 将构造函数和非成员函数虚拟化
- 8.将构造函数和非成员函数“虚拟化”
- 将构造函数和非成员函数虚拟化
- sizeof,字节对齐,多继承,虚拟继承,成员函数指针
- Item 36 非虚拟的成员函数不能重定义
- More Effective C++----技巧 & (25)将构造函数和非成员函数虚拟化
- More Effective C++ -----将构造函数和非成员函数虚拟化
- 关键字:sizeof,字节对齐,多继承,虚拟继承,成员函数指针
- 深度搜索C++对象模型 Function 语意学-虚拟成员函数
- C++对象模型——Virtual Member Functions (虚拟成员函数)(第四章)
- 【C++】深度探索C++对象模型之虚拟成员函数(virtual member function)
- 虚拟函数
- 虚拟函数
- 成员函数
- 【读书笔记】iOS-Xcode-查找特殊字符的方法
- Knockout.js on the way
- Volley详解(四)——缓存(Cache)
- he
- 通过lldb远程调试iOS App
- 4.2 虚拟成员函数
- FMDB简单应用
- servlet
- DroidBox 环境搭建 【图文】
- 使用fis对资源进行打包、压缩、合并、加MD5发布
- 用户登陆登出日志
- js中的"=="和equals()以及is()三者的区别
- OC - ClassAndObject
- Ubuntu下git使用