C++从子类访问父类的私有函数
来源:互联网 发布:vmware mac os x补丁 编辑:程序博客网 时间:2024/05/16 09:13
C++:从子类访问父类的私有函数
转自 龙音阁http://blog.sina.com.cn/dragonsound 感谢原作者的工作
(2009-09-01 22:44:21)
标签: 虚函数 c private f1 it 众所周知,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 0;
}
转自 龙音阁http://blog.sina.com.cn/dragonsound 感谢原作者的工作
(2009-09-01 22:44:21)
标签: 虚函数 c private f1 it 众所周知,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 0;
}
- C++:从子类访问父类的私有函数
- C++从子类访问父类的私有函数
- C++从子类访问父类的私有函数
- 从子类访问父类的私有函数
- 对于父类的私有属性,子类是从哪里访问到的?
- 子类无权访问父类的私有数据成员
- 关于Java子类访问父类的私有变量
- 子类能否继承、访问父类的私有成员
- JAVA在子类中访问父类的私有属性
- Android,子类访问父类私有成员
- 子类会继承父类的私有方法和私有属性,只是不能直接访问
- 子类使用构造函数初始化父类的私有数据
- [c++]子类私有的虚函数,多态问题
- 子类继承父类的私有属性
- 子类继承父类的私有属性
- 子类继承父类的私有成员
- 子类继承父类的私有属性
- GObject 子类私有属性的外部访问
- android4.0.3编译不过的原因
- linux环境下jdk+eclipse+ndk环境搭建
- 腾讯笔试:把两个数和告诉A,积告诉B,求这两个数是什么
- linux开机启动
- #pragma once与 #ifndef的区别
- C++从子类访问父类的私有函数
- php getopt
- 常用 Java 静态代码分析工具的分析与比较
- 形参个数可变的方法
- RTX51学习心得
- sql server 死锁总结
- 基于android2.3.5系统:开天辟地Android启动机制[三]
- 在链表里如何发现循环链接?
- 黑马程序员——JAVA数组