滴水逆向笔记系列-c++总结3-39.模板-40.引用_友元_运算符重载

第三十八课 c++6 模板

1.冒泡排序和折半查找

void Sort(int* arr,int nLength)						
{						
	int i;					
	int k;					
	for(i=0;i<nLength-1;i++)					
	{					
		for(k=0;k<nLength-1-i;k++)				
		{				
			if(arr[k]>arr[k+1])			
			{			
				int temp = arr[k];		
				arr[k] = arr[k+1];		
				arr[k+1] = temp;		
			}			
		}				
	}					
}						
int Find(int* arr,int nLength,int nElement)							
{							
	int nBegin = 0,nEnd = nLength-1,nIndex;						
	while(nBegin<=nEnd)						
	{						
		nIndex = (nBegin+nEnd)/2;//(nBegin+nEnd)>>1					
		if(nElement > arr[nIndex])					
		{					
			nBegin = nIndex+1;				
		}					
		else if(nElement < arr[nIndex])					
		{					
			nEnd = nIndex-1;				
		}					
		else					
		{					
			return nIndex;				
		}					
	}						
	return -1;						
}

2.模板的作用

先看看下面两种模板的使用

在函数中的使用

函数中传参什么T就代表什么类型

template<class T>				
void Sort(T arr,int nLength)				
{				
	int i;			
	int k;			
	for(i=0;i<nLength-1;i++)			
	{			
		for(k=0;k<nLength-1-i;k++)		
		{		
			if(arr[k]>arr[k+1])	
			{	
				int temp = arr[k];
				arr[k] = arr[k+1];
				arr[k+1] = temp;
			}	
		}		
	}			
}				

在类或结构体中的使用

类声明时<>表明什么类型就代表什么类型

template<class T,class M>				
struct Base				
{				
	T x;			
	T y;		
	M a;			
	M b;			
				
	T Max()			
	{			
		if(x>y)		
		{		
			return x;	
		}		
		else		
		{		
			return y;	
		}		
	}			
	M Min()			
	{			
		if(a<b)		
		{		
			return a;	
		}		
		else		
		{		
			return b;	
		}		
	}			
};				
int main(int argc, char* argv[])				
{				
	Base<int,char> base;			
	base.x = 10;			
	base.y = 20;			
	base.a = 1;			
	base.b = 2;			
				
	int x = base.Max();		
	int y = base.Min();			

	printf("%d %d\n",x,y);			
	return 0;			
}				

3.模板的反汇编

用不用模板从底层反汇编来看都是一样的
image.png

作业

image.png
1、函数模板传指针的情况

#include <stdio.h>
template <class T>
void Swap(T& x,T& y)
{
	T temp = x;
	x = y;
	y = temp;
}
int main()
{
	int x = 1;
	int y = 2;
	Swap(x,y);
	printf("%d , %d ",x,y);
}

3、可以看到还是调用了不同的函数地址,而且根据上面看到了用不用模板的反汇编代码都是一样的,所以可以看出模板的本质就是代码的复制,编译器帮我们对不同的类型多复制了几个函数
image.png

第三十九课 c++7 引用-友元- 运算符重载

1.引用

在函数参数传递时反汇编是没有区别的
image.png
看了这个我才能真理解引用就是变量别名的意思
而且初始化时一定要赋值

// 基本类型
int x = 1;
int& ref = x;
ref = 2;
printf("%d \n",ref);
 
// 类
Person p;
Person& ref = p;
ref.x = 10;
printf("%d \n",p.x);
 
// 指针类型
int****** x = (int******)1;
int******& ref = x;
ref = (int******)2;
printf("%d \n",x);
 
// 数组类型
int arr[] = {1,2,3};
int (&p)[3] = arr;
p[0] = 4;
printf("%d \n",arr[0]);

指针和引用

struct Base			
{			
	int x;		
	int y;		
	Base(int x,int y)		
	{		
		this->x = x;	
		this->y = y;	
	}		
};			
void PrintByPoint(Base* pb)								
{								
	printf("%d  %d\n",pb->x,pb->y);							
								
	pb = (Base*)0x123456;							
								
	//为所欲为...							
}								
void PrintByRef(Base& refb,Base* pb)								
{								
	printf("%d  %d\n",refb.x,refb.y);							
	Base b1(21,31);							
	//&refb = b1; //引用不能重新赋值							
	refb = b1;  //这个不是重新赋值,这个是把b1的值赋给refb代表的对象							
	printf("%d  %d\n",pb->x,pb->y);							
}		

int main(int argc, char* argv[])			
{			
	Base base(1,2);		
			
	PrintByRef(base,&base);	
	return 0;		
}			

可以看一下反汇编帮助理解一下,b1对象的值赋给了refb这个base的别名
image.png
有时候为了避免引用可以被修改,可以把引用声明为常量,不可修改
image.png

总结:

  1. 引用必须赋初始值,且只能指向一个变量,从一而终(专一)
  2. 对引用赋值,是对其指向的变量赋值,而不是修改引用本身的值;
  3. 对引用做运算,就是对其指向的变量做运算,而不是对引用本身做运算;
  4. 引用类型只能赋值一次,不能重新赋值
  5. 使用引用可以像指针那样去访问、修改对象的内容,但更加安全,但是不灵活

2.友元

当类内有两个私有成员,我们类外函数想直接访问时可以在类内声明这个类外函数是友元函数,那么友元函数就可以直接访问类内的私有成员

示例代码

class Person				
{				
private:				
	int x;			
	int y;			
public:				
	Person(int x,int y)			
	{			
		this->x = x;		
		this->y = y;		
	}			
	//声明友元函数			
	//friend void Print(const  Person& refPer);			
};				
void Print(const Person& refPer)				
{				
	printf("%d\n",refPer.x);			
	printf("%d\n",refPer.y);			
}				
int main(int argc, char* argv[])				
{				
	Person p(1,2);			
				
	Print(p);			
				
	return 0;			
}

总结:

什么情况下需要友元函数:

  • 运算符重载的某些场合需要使用友元.
  • 两个类要共享数据的时候.

友元函数和类的成员函数的区别

  • 成员函数有this指针,而友元函数没有this指针
  • 友元函数是不能被继承的,就像父亲的朋友未必是儿子的朋友

3.运算符重载

class Number		
{		
private:		
	int lowValue;	
	int highValue;	
public:		
	Number(int lowValue,int highValue);	
	void Print();	
	Number operator++();	
	Number operator--();	
	Number operator+(const Number& p);	
	Number operator-(const Number& p);	
	Number operator*(const Number& p);	
	Number operator/(const Number& p);	
	bool operator>(const Number& p);	
	bool operator<(const Number& p);	
	bool operator==(const Number& p);	
};		
Number::Number(int lowValue,int highValue)		
{		
	this->lowValue = lowValue;	
	this->highValue = highValue;	
}		
void Number::Print()		
{		
	printf("%d\n",lowValue);	
	printf("%d\n",highValue);	
}		
Number Number::operator++()		
{		
	lowValue++;	
	highValue++;	
	return *this;	
}		
Number Number::operator--()		
{		
	lowValue--;	
	highValue--;	
	return *this;	
}		
Number Number::operator+(const Number& p)		
{		
	this->highValue = this->highValue + p.highValue;	
	this->lowValue = this->lowValue + p.lowValue;	
	return *this;	
}		
Number Number::operator-(const Number& p)		
{		
	this->highValue = this->highValue - p.highValue;	
	this->lowValue = this->lowValue - p.lowValue;	
	return *this;	
}		
Number Number::operator*(const Number& p)		
{		
	this->highValue = this->highValue * p.highValue;	
	this->lowValue = this->lowValue * p.lowValue;	
	return *this;	
}		
Number Number::operator/(const Number& p)		
{		
	this->highValue = this->highValue / p.highValue;	
	this->lowValue = this->lowValue / p.lowValue;	
	return *this;	
}		
bool Number::operator>(const Number& p)		
{		
	if(this->highValue > p.highValue)	
	{	
		return true;
	}	
	return false;	
}		
bool Number::operator<(const Number& p)		
{		
	if(this->highValue < p.highValue)	
	{	
		return true;
	}	
	return false;	
}		
bool Number::operator==(const Number& p)		
{		
	if(this->highValue == p.highValue)	
	{	
		return true;
	}	
	return false;	
}		
void Test()		
{		
	Number p(1,2),p2(3,4);	
	p++;	
	p.Print();	
	p--;	
	p.Print();	
		
	p = p+p2;	
	p.Print();	
		
}		
int main(int argc, char* argv[])		
{		
	Test();	
}		

作业

image.png
1、

#include <stdio.h>
class Math
{
private:
	int x ;
	int y ;
public:
	Math(int x,int y)
	{
		this->x = x;
		this->y = y;
	}
	friend Math& operator++(Math& m,int);
	friend bool operator==(Math& m, Math& t);
	friend void print(const Math& m);
};
Math& operator++(Math &m,int)
{
	m.x++;
	m.y++;
	return m;
}
bool operator==(Math& m, Math& t)
{
	if (m.x == t.x && m.y == t.y)
	{
		return true;
	}
	else
	{
		return false;
	}
}
void print(const Math& m)
{
	printf("%d %d\n", m.x, m.y);
}
int main()
{
	Math m(1, 2);
	Math m1(2,3);
	//m++;
	if (m == m1)
	{
		printf("相等\n");
	}
	else
	{
		printf("不相等\n");
	}
	print(m);
}

总结:

  • 友元函数去重载运算符应该传一个 Math 对象的引用作为参数,因为友元函数不像在类内声明的函数会自动传一个this指针
  • friend Math& operator++(Math& m,int);需要在传参时加一个占位参数int区分前缀自增还是后缀自增
  • Math&返回类型也使用引用,方便后置递增操作
  • void print(const Math& m)打印函数应该传静态的参数防止不小心被修改了
posted @ 2024-03-16 10:50  小新07  阅读(52)  评论(0编辑  收藏  举报