首页 > 编程语言 >滴水逆向笔记系列-c++总结2-36.权限控制-37.虚函数-38.多态_绑定

滴水逆向笔记系列-c++总结2-36.权限控制-37.虚函数-38.多态_绑定

时间:2024-03-16 11:15:09浏览次数:18  
标签:... 38 void 37 多态 virtual printf public Fn

第三十六课 c++3 权限控制

1.定义和实现分开写

image.pngimage.png

2.private和public

private权限说明

私有变量在类外是无法访问的,只有在类内或者使用类内函数访问
image.png

类内函数访问

image.png

3.private真的不能访问吗

反汇编看看
image.png
t对象在初始化public和private成员时都是一视同仁的,在底层还是没区别,都是编译器给的限制
image.png
image.png

4.class和struct的区别

class默认都是private权限,struct默认都是public权限
image.pngimage.png
class继承的一致默认所有都是private,父类public的成员继承过来也变成private权限
如果class Sub:public Base,那么也只能访问父类的public成员,private成员还是不行
image.png
子类把父类的private继承过去了,在同一个内存里,但是它只能在那摆着,编译器不允许访问,但是从底层调用指针可以访问
image.png

第三十七课 c++4 虚函数

1.虚函数:间接调用

#include <stdio.h>
class Base
{
public:
    void Function_1()
    {
        printf("Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Function_2...\n");
    }
};
int main()
{
    Base base;
    base.Function_1();
    base.Function_2();
}

image.pngimage.png

总结:

  • 使用对象调用虚函数和正常函数没有任何区别

  • 使用指针调用虚函数是间接call,调用正常函数的直接call

2.深入虚函数调用

我们可以发现在类中,如果使用了虚函数,不管使用了几个虚函数都只会多四个字节,我们现在需要探索一下这四个字节是什么
image.png
反汇编看一下间接调用了什么
image.png
调用两个虚函数时,他call edx+4就是第二个虚函数的地址
image.png

总结:

  • 当类中有虚函数时,不管有几个虚函数,类的最前面会多出四个字节

  • 这四个字节就是虚函数表的地址,虚函数表存放了类内所有虚函数的函数地址

3.打印虚函数表

#include <stdio.h>
class Base
{
public:
    int x;
    int y;
    virtual void Function_1()
    {
        printf("Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Function_3...\n");
    }
};

int main()
{
    //查看 Sub 的虚函数表				
    Base base;
    //对象的前四个字节就是虚函数表				
    printf("base 的虚函数表地址为:%x\n", *(int*)&base);
    //通过函数指针调用函数,验证正确性				
    typedef void(*pFunction)(void);
    pFunction pFn;
    for (int i = 0; i < 3; i++)
    {
        int temp = *((int*)(*(int*)&base) + i);
        pFn = (pFunction)temp;
        pFn();
    }
}

注意:

  • &base就是对象的首地址

  • (int)&base才是虚函数的地址

  • ((int)((int)&base))解引用出第一个虚函数的地址,然后使用函数指针去调用这个虚函数地址**

  • ((int)((int)&base) + i)解引用出第i个虚函数的地址

总结:

image.png

作业

1、单继承无函数覆盖

虚函数是会继承的
image.png

2、单继承有函数覆盖

输出后程序会崩溃,因为虚函数表只有4个函数地址,我打印了六个,第五个开始内存都是0,调用不了
image.png
image.png

3、多继承无函数覆盖

(多个直接父类时有多张虚表且子类虚函数在第一个虚表里)

struct Base1							
{							
public:							
    virtual void Fn_1()							
    {							
        printf("Base1:Fn_1...\n");							
    }							
    virtual void Fn_2()							
    {							
        printf("Base1:Fn_2...\n");							
    }							
};							
struct Base2							
{							
public:							
    virtual void Fn_3()							
    {							
        printf("Base2:Fn_3...\n");							
    }							
    virtual void Fn_4()							
    {							
        printf("Base2:Fn_4...\n");							
    }							
};							
struct Sub:Base1,Base2							
{							
public:							
    virtual void Fn_5()							
    {							
        printf("Sub:Fn_5...\n");							
    }							
    virtual void Fn_6()							
    {							
        printf("Sub:Fn_6...\n");							
    }							
};							
int main(int argc, char* argv[])							
{							
	//查看 Sub 的虚函数表						
    Sub sub;							
	//通过函数指针调用函数,验证正确性						
    typedef void(*pFunction)(void);											
	//对象的前四个字节是第一个Base1的虚表						
	printf("Sub 的虚函数表地址为:%x\n",*(int*)&sub);										
	pFunction pFn;									
	for(int i=0;i<6;i++)						
	{						
		int temp = *((int*)(*(int*)&sub)+i);					
		if(temp == 0)					
		{					
			break;				
		}					
		pFn = (pFunction)temp;					
		pFn();					
	}										
	//对象的第二个四字节是Base2的虚表						
	printf("Sub 的虚函数表地址为:%x\n",*(int*)((int)&sub+4));											
	pFunction pFn1;									
	for(int k=0;k<2;k++)						
	{						
		int temp = *((int*)(*(int*)((int)&sub+4))+k);					
		pFn1 = (pFunction)temp;					
		pFn1();					
	}												
}							

image.png

4、多继承有函数覆盖

两个父类的虚函数先放两张表里,然后子类虚函数覆盖上去

struct Base1			
{			
public:			
    virtual void Fn_1()			
    {			
        printf("Base1:Fn_1...\n");			
    }			
    virtual void Fn_2()			
    {			
        printf("Base1:Fn_2...\n");			
    }			
};			
struct Base2			
{			
public:			
    virtual void Fn_3()			
    {			
        printf("Base2:Fn_3...\n");			
    }			
    virtual void Fn_4()			
    {			
        printf("Base2:Fn_4...\n");			
    }			
};			
struct Sub:Base1,Base2			
{			
public:			
    virtual void Fn_1()			
    {			
        printf("Sub:Fn_1...\n");			
    }			
    virtual void Fn_3()			
    {			
        printf("Sub:Fn_3...\n");			
    }			
	virtual void Fn_5()		
    {			
        printf("Sub:Fn_5...\n");			
    }			
};			

image.png

5、多重继承无函数覆盖

image.png

6、多重继承有函数覆盖

后来的覆盖前面的

struct Base1			
{			
public:			
    virtual void Fn_1()			
    {			
        printf("Base1:Fn_1...\n");			
    }			
    virtual void Fn_2()			
    {			
        printf("Base1:Fn_2...\n");			
    }			
};			
struct Base2:Base1			
{			
public:			
    virtual void Fn_1()			
    {			
        printf("Base2:Fn_1...\n");			
    }			
    virtual void Fn_3()			
    {			
        printf("Base2:Fn_3...\n");			
    }			
};			
struct Sub:Base2			
{			
public:			
	virtual void Fn_5()		
    {			
        printf("Sub:Fn_5...\n");			
    }			
};			

image.png

struct Base1			
{			
public:			
    virtual void Fn_1()			
    {			
        printf("Base1:Fn_1...\n");			
    }			
    virtual void Fn_2()			
    {			
        printf("Base1:Fn_2...\n");			
    }			
};			
struct Base2:Base1			
{			
public:			
    virtual void Fn_1()			
    {			
        printf("Base2:Fn_1...\n");			
    }			
    virtual void Fn_3()			
    {			
        printf("Base2:Fn_3...\n");			
    }			
};			
struct Sub:Base2			
{			
public:			
	virtual void Fn_1()		
    {			
        printf("Sub:Fn_1...\n");			
    }			
	virtual void Fn_5()		
    {			
        printf("Sub:Fn_5...\n");			
    }			
};			

image.png

第三十七课 c++5 多态-绑定

1.什么是绑定

编译器绑定(前期绑定)

变量x和funciton_1就属于编译器绑定,一编译完调用普通成员变量的地方的变量地址已经确定了

动态绑定(后期绑定)

function_2就是动态绑定,编译完调用函数的地方还没确定地址,这里只是call虚表第一个函数中的值,只有运行时确定虚表第一个值是多少
image.png

多态例子一:

class Base				
{				
public:				
	int x;			
public:				
	Base()			
	{			
		x = 100;		
	}			
    void Function_1()				
    {				
        printf("Base:Function_1...\n");				
    }				
    virtual void Function_2()				
    {				
        printf("Base:Function_2...virtual\n");				
    }				
};				
class Sub:public Base				
{				
public:				
	int x;			
public:				
	Sub()			
	{			
		x = 200;		
	}			
    void Function_1()				
    {				
        printf("Sub:Function_1...\n");				
    }				
    virtual void Function_2()				
    {				
        printf("Sub:Function_2...virtual\n");				
    }				
};				
				
void TestBound(Base* pb)				
{				
	int n = pb->x;			
	printf("%x\n",n);			
				
	pb->Function_1();		//函数调用	
				
	pb->Function_2();			
}				
int main(int argc, char* argv[])				
{						
	return 0;			
}				

注意:
一直想不懂为什么传入sub对象时pb->x依然是100?

  • 因为是子类对象传入父类指针,现在子类对象继承了父类的变量,这就涉及到继承的父子类变量重名问题,子类对象里面照样把重名的变量继承过来,所以现在子类对象里有两个x变量的值,但是我们现在用父类指针去访问,所以访问出父类的变量x

多态例子二:(父类指针数组指向多个子类对象)

class Base
{
public:
	int x;
	int y;
public:
	Base()
	{
		x = 1;
		y = 2;
	}
	virtual void print()
	{
		printf("Base:%d %d \n",x,y);
	}
};
class Sub1 :public Base
{
public:
	int A;
public:
	Sub1()
	{
		x = 3;
		y = 4;
		A = 7;
	}
	virtual void print()
	{
		printf("Sub1: %d %d %d\n", x, y,A);
	}
};
class Sub2 :public Base
{
public:
	int B;
public:
	Sub2()
	{
		x = 5;
		y = 6;
		B = 8;
	}
	virtual void print()
	{
		printf("Sub2:%d %d %d\n", x, y,B);
	}
};

void TestBound()
{
	/*int n = pb->x;
	printf("%x\n", n);*/
	Base base;
	Sub1 sub1;
	Sub2 sub2;

	Base* arr[] = { &base,&sub1,&sub2 };
	for (int i = 0; i < 3; i++)
	{
		arr[i]->print();
	}
	
}
int main(int argc, char* argv[])
{
	TestBound();
	return 0;
}

这是输出结果
image.png

注意(重点):

问题一:为什么只输出两个数?(上面代码还没加virtual虚函数时)
因为我们没有使用虚函数,一直调用的是父类函数,父类函数我们只输出了x和y
image.png
问题二:修改后为什么这里会输出sub1和sub2各自的xy变量,例子一不是说父类指针调用父类的xy吗?
在两个sub对象中,由于我们没有重新声明x和y变量,所以在sub对象内存空间里只有x,y,A三个变量,而且在sub的构造函数中给x和y重新赋值了。然而在例子一中,我们在父子类都声明了变量x和y,所以子类内存空间里会有五个变量,(x,y,x,y,A),而后用父类指针访问自然就是打印了父类的变量x和y。
下面是两种情况的内存情况
image.png
image.png

2.有继承关系中的析构函数最好使用virtual

因为释放内存等操作时最好释放当前类的内存

3.总结

  • 一种类型能体现多种行为的,就叫多态,通过父类指针可以指向父类或者子类对象,就可能导致同一个虚函数function_2体现出不同的行为
  • 多态 = 动态绑定
  • c++的动态绑定是通过虚表实现的

标签:...,38,void,37,多态,virtual,printf,public,Fn
From: https://www.cnblogs.com/xiaoxin07/p/18076806

相关文章

  • 389. 找不同c
       charfindTheDifference(char*s,char*t){inttemps[26]={0};inttempt[26]={0};intn1=strlen(s),n2=strlen(t);for(inti=0;i<n1;i++)temps[s[i]-'a']++;for(inti=0;i<n2;i++)tempt[t[i]-'a']++;int......
  • 通天星CMSV6车载定位监控平台 SQL注入漏洞复现(XVE-2023-23744)
    0x01产品简介通天星CMSV6车载定位监控平台拥有以位置服务、无线3G/4G视频传输、云存储服务为核心的研发团队,专注于为定位、无线视频终端产品提供平台服务,通天星CMSV6产品覆盖车载录像机、单兵录像机、网络监控摄像机、行驶记录仪等产品的视频综合平台。0x02漏洞概述该漏洞......
  • 力扣刷题Days19-637.二叉树的层平均数
    目录1,题目2,代码2.1广度优先遍历2.2深度优先遍历3,学习与总结1,题目给定一个非空二叉树的根节点 root ,以数组的形式返回每一层节点的平均值。2,代码2.1广度优先遍历/***Definitionforabinarytreenode.*functionTreeNode(val,left,right){*......
  • 洛谷题解 - B3850 [GESP202306 四级] 幸运数
    目录题目描述输入格式输出格式样例#1样例输入#1样例输出#1代码题目描述小明发明了一种“幸运数”。一个正整数,其偶数位不变(个位为第111位,十位为第......
  • 1702967-37-0 PSMA617是一种具有多种功能的化合物
    描述:PSMA617也被称为vipivotidetetraxetan,是一种具有多种功能的化合物。它主要用于制造177Lu-PSMA-617,这是一种抗癌症的放射性分子。PSMA617具有一种小肽,设计用于靶向前列腺特异性膜抗原(PSMA)。PSMA617是一种功能强大的化合物,在治研究领域具有广泛的应用前景。英文名称:PSMA6......
  • 代码随想录算法训练营第四十七天| ● 198.打家劫舍 ● 213.打家劫舍II ● 337.打家
    打家劫舍 题目链接:198.打家劫舍-力扣(LeetCode)思路:每一家的最大收益来源只有两个,一个是这家不偷,那么最大收益等于从上一家出来的最大收益,另一个是偷这一个家,因此最大收益等于从上上一家出来的最大收益加这一家的收益。classSolution{public:introb(vector<int>&nu......
  • 西门子人机界面维修SIEMENS 6AV6 644-0BA01-2AX1 MP 377工控机
    SIMATICHMI面板–轻松实现面向机器的操作当人们需要使用机器和设备执行各种任务时,就需要监控和操作员控制设备。在面向机器的操作和监控方面,SIMATICHMI面板始终是首选。SIMATICHMI面板产品组合:适应性强、可扩展、面向未来在人与机器或操作员与流程之间的接口处,数字......
  • 代码随想录算法训练营第四十七天 | 337.打家劫舍III,213.打家劫舍II ,198.打家劫舍
     198.打家劫舍 已解答中等 相关标签相关企业 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一......
  • 代码随想录训练营第44天 | 动态规划:完全背包理论基础、​​​​​​LeetCode 518.零钱
    目录动态规划:完全背包理论基础文章讲解:代码随想录(programmercarl.com)视频讲解:带你学透完全背包问题!_哔哩哔哩_bilibili思路​​​​​​LeetCode518.零钱兑换II文章讲解:代码随想录(programmercarl.com)视频讲解:518.零钱兑换II_哔哩哔哩_bilibili思路​​​​​​Le......
  • 3745. 牛的学术圈 I(acwing)
    文章目录3745.牛的学术圈I题目描述h指数的解释与计算二分查找3745.牛的学术圈I题目描述由于对计算机科学的热爱,以及有朝一日成为「Bessie博士」的诱惑,奶牛Bessie开始攻读计算机科学博士学位。经过一段时间的学术研究,她已经发表了N篇论文,并且她的第i篇......