转:C++:从子类访问父类的私有函数
众所周知,c和c++的数组都是不安全的,因为无论c还是c++都不提供数组边界检查功能,这使得数组溢出成为可能。
从某个意义上说,c和c++是一种缺少监督的语言,然而这也正是其魅力所在。c++给予程序员更大的自由,相比于使用JAVA编程的束手束脚,c++程序员拥有了更大的权力,同时也拥有更多的机遇来玩弄一些技巧,比如说,从子类调用父类的私有函数。
从子类调用父类的private函数?我没听错么?
当然没有!
尽管从各种c++书籍中我们得到的信息是子类从父类继承的仅有protected成员和public成员,而父类的private成员无法被子类继承,也无法被子类访问,但是当父类的private函数是一个虚函数时,我们却可以通过读取VTABLE表中信息,从而找到父类虚函数的地址,进而调用它。
先回忆一下,c++的多态是怎样实现的。
当c++的类中出现virtual关键字时,该类就拥有了一张VTABLE表。VTABLE表的内容包括了各个虚函数在虚拟内存中的偏移量,也就是说如果类A拥有三个虚函数:f1,f2,f3,那么在类A的虚函数表VTABLE中将依次存放f1,f2,f3的偏移地址。
那么,当类A仅拥有一个虚函数时,类A实例所占内存大小,也就是sizeof(A)与类A拥有两个、三个,甚至更多个虚函数时的sizeof(A)有区别么?不,毫无区别。如果一个类的所有成员都是被virtual修饰的虚函数,那么当您使用sizeof(A)查看其大小时,结果无一例外的都是4——在32位系统中,四字节恰恰是一个整型数的大小,也恰恰是一个指针的大小。
这是为什么?
因为对于类A而言,它并不需要知道有多少个虚函数,它需要的仅仅是一个指向VTABLE的指针,这个指针通常被叫作vptr。它指向了VTABLE,至于究竟有多少个虚函数,只需在VTABLE中寻找就是。让我们想象一下,vptr就向一个路标,它指向一个名叫VTABLE的公司,至于公司里有多少员工,你必须进入VTABLE公司才会知道。
好了,现在我们总结一下:
结论一:在有虚函数的类中,一定有一个vptr指向VTABLE
结论二:VTABLE中依次存储了各个虚函数在虚拟内存中的偏移地址
现在,我们再来介绍另两个c++的规律。
规律一:在任何类中,vptr一定存储在该类实例的前四个字节中。
规律二:当子类和父类同时拥有虚函数时,子类的VTABLE中也同时会拥有父类和子类的虚函数偏移地址,而且子类的虚函数偏移地址一定是追加在父类虚函数偏移地址之后的。
也就是说,如果有如下两个类:
class A {
private:
virtual void WhoAmI() {
cout << "I am class A" << endl;
}
};
class B:public A {
public:
void WhoAmIForB() {
cout << "I am class B" << endl;
}
};
那么,实例
A a;
B b;
中各有一个vptr,其中a的vptr为(int*)(*(int*)(&a)),而b的vptr为(int*)(*(int*)(&b)),
这两个vptr又分别指向各自的VTABLE,其中父类A的VTABLE中存储的内容是:A::WhoAmI的偏移地址,而子类B的VTABLE呢?
子类B的VTABLE中依次存储了A::WhoAmI的偏移地址,B::WhoAmIForB的偏移地址。
注意了,关键就在这里:A的虚函数都是私有的,不是么?但是编译器链接器在此时却似乎将关键字private忘记了,无论这些虚函数是private还是public的,它们的偏移地址都毫无例外的存放在了子类的VTABLE中!
这就是破绽!你可以刺出至命的一剑了!
我们既然知道子类实例的vptr,为什么不能推算出子类的VTABLE?
既然知道子类的VTABLE,根据规定律二,为什么不能推算出父类的虚函数偏移量?
答案就是:父类的第一个虚函数所在偏移量是(int*)(*(子类的vptr)),也就是——(int*)(*(int*)(*(int*)(&b)))。
当我们强制将其转换为一个指向函数的指针时,就可以调用它,从而实现了从子类调用父类私有函数的行为。
试运行如下一段代码:
#include <iostream>
using namespace std;
class A {
private:
virtual void WhoAmI() {
cout << "I am class A" << endl;
}
virtual void f0() {
cout << "This is f0" << endl;
}
virtual void f1() {
cout << "This is f1" << endl;
}
};
class B:public A {
public:
void WhoAmIForB() {
cout << "I am class B" << endl;
}
};
typedef void (*FUNC)();
int main(int argc,char * argv[])
{
B b;
b.WhoAmIForB();
//b.WhoAmI(); error C2248: “A::WhoAmI”: 无法访问 private 成员(在“A”类中声明)
FUNC func = (FUNC)((int*)(*(int*)(*(int*)(&b))));
func();
return ;
}
转:C++:从子类访问父类的私有函数的更多相关文章
- Java子类访问父类的私有成员变量
/**子类会继承父类所有的属性和方法. * 但是根据不同的权限标识符,子类不可见父类的私有变量,但可以通过父类的公共方法访问私有变量 * 所以对于重名变量,子类和父类都各有一份. * 对于子类和父类中 ...
- javascript中uber实现子类访问父类成员
function Animal(){} Animal.prototype={ name:"animal", toString:function(){ console.log(thi ...
- C++ 学习笔记 (六) 继承- 子类与父类有同名函数,变量
学习了类的继承,今天说一下当父类与子类中有同名函数和变量时那么程序将怎么执行.首先明确当基类和子类有同名函数或者变量时,子类依然从父类继承. 举例说明: 例程说明: 父类和子类有同名的成员 data: ...
- C++ 子类继承父类纯虚函数、虚函数和普通函数的区别
C++三大特性:封装.继承.多态,今天给大家好好说说继承的奥妙 1.虚函数: C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现.子类可以重写父类的虚函数实现子类 ...
- spring使用注解通过子类注入父类的私有变量
方法一 通过 super.setBaseDao方法设置父类私有变量 父类 public class BaseServiceImpl { private BaseDao baseDao; publ ...
- 子类覆写的变量被private隐藏,强制转换方式通过子类访问父类的被覆写变量:
import static java.lang.System.*; public class SuperParent{ public static void main(String[] args){ ...
- 【C++】子类访问父类typedef的问题
class A { public: typedef int* pointer; }; class B :public A { public: pointer b; }; 这段代码运行没有问题,子类继承 ...
- OC 继承子类对象调用方法机制 子类对象访问父类中的实例变量
在继承中,子类对象如何调用到正确方法的机制 每一个Objective - C对象都有一个隐藏的指针指向类的代码,当向一个对象发送消息的时候,当前的对象会首先在当前类里去查找相应的方法,如果找到的话,直 ...
- Java vs C++:子类覆盖父类函数时缩小可访问性的不同设计
Java 和 C++ 都是面向对象的语言,允许对象之间的继承.两个语言的继承都设置有允许子类覆盖父类的“虚函数”,加引号是因为 Java 中没有虚函数这一术语,但是我们的确可以把 Java 的所有函数 ...
随机推荐
- Echoprint系列--Android编译与调用
在Echoprint系列--编译中编译了源代码,这次将Echoprint移植到Android平台并測试识别歌曲功能. 一.编译库 1.环境准备 Android NDK,我的是android-ndk-r ...
- 经常使用的时间同步server地址
转载出处http://www.minunix.com/2013/03/ntpserver/ 感谢原作者,一切的权利都属于原作者,假设有所不适,我会马上删除 中国大概能用的NTP时间server地址 s ...
- HTML5新特性之CSS+HTML5实例
1.新的DOCTYPE和字符集 HTML5的一项准则就是化繁为简,Web页面的DOCTYPE被极大的简化. <!DOCTYPE html> 同时字符集声明也被简化了: <meta c ...
- Head First SQL笔记
看的时候总结了一下,如下: Chapter 1: 创建数据库 CREATE DATABASE database_name; 使用数据库 USE database_name; 创建表 CRATE TAB ...
- javascript条件运算符
variablename=(condition)?value1:value2 javascript条件运算符
- windows service 的创建 安装 调试 错误回发
关于如何快速创建一个windows服务 1.在vs中创建windows服务 名称:你要写的服务名称 位置:创建服务所在的位置 点击确定 2.代码编写 3.添加安装程序 点击添加安装程序出现 分别右击设 ...
- UVa12563(DP)
题意:求在给定时间内,最多能唱多少歌曲,在最多歌曲的情况下,使唱的时间最长. 该题类似于01背包问题,可用01背包问题的解题思路来求,每个歌曲相当于物品,歌曲的长度相等于物品重量,每个歌曲的“价值”为 ...
- mysql查询数据库中包含某字段(列名)的所有表
SELECT TABLE_NAME '表名',TABLE_SCHEMA '数据库名',ORDINAL_POSITION '顺序',COLUMN_NAME '字段',DATA_TYPE '类型' ,CH ...
- uncompyle2 windows安装和使用方法
uncompyle2是Python 2.7的反编译工具,它可以把python生成的pyo.pyc字节码文件反编译为十分完美的源码,并可以将反编译后的源码再次生成字节码文件! ----- 本文介绍在wi ...
- Flask源码解读(一)
Flask是一个使用 Python 编写的轻量级 Web 应用框架.Flask 本身只是 Werkezug 和 Jinja2 的之间的桥梁,前者实现一个合适的 WSGI 应用,后者处理模板. 当然, ...