文章

滴水-day34-引用-友元-运算符重载

引用

引用是什么?

void Test(int x, int y)
{
	x = 1;
	y = 2;
}
int main()
{
	int x = 2;
	int y = 2;
	Test(x, y);
	cout << x << " " << y << endl;
	return 0;
}

x和y的值并不会改为1和2,因为在函数传递参数的时候只是把x和y复制了一份传递给了Test函数,在Test函数中修改的x和y是main函数中x,y的复制体。

0041EB55 C7 45 F8 02 00 00 00 mov         dword ptr [ebp-8],2  
0041EB5C C7 45 EC 02 00 00 00 mov         dword ptr [ebp-14h],2  
0041EB63 8B 45 EC             mov         eax,dword ptr [ebp-14h]  
0041EB66 50                   push        eax  
0041EB67 8B 4D F8             mov         ecx,dword ptr [ebp-8]  
0041EB6A 51                   push        ecx  
0041EB6B E8 AF 28 FF FF       call        0041141F 

Test:
0041EAF1 C7 45 08 01 00 00 00 mov         dword ptr [ebp+8],1  
0041EAF8 C7 45 0C 02 00 00 00 mov         dword ptr [ebp+0Ch],2

通过汇编代码观察发现是把ebp+8和ebp+0xch处的值改为了1和2,这两个值是Test函数中局部变量的地址并不会影响到外部代码中x和y的值。

如何在没有返回值的函数中修改传递参数的值?

void Test(int* x, int* y)
{
	*x = 3;
	*y = 3;
}

int main()
{
	int x = 2;
	int y = 2;
	Test(&x, &y);
}

通过指针的方式去修改参数,此时可以影响到外部参数的原本值。

0041332F C7 45 F4 02 00 00 00 mov         dword ptr [ebp-0Ch],2  
00413336 C7 45 E8 02 00 00 00 mov         dword ptr [ebp-18h],2
0041333D 8D 45 E8             lea         eax,[ebp-18h]  
00413340 50                   push        eax  
00413341 8D 4D F4             lea         ecx,[ebp-0Ch]  
00413344 51                   push        ecx  
00413345 E8 51 E2 FF FF       call        0041159B  

Test:
0041EAF1 8B 45 08             mov         eax,dword ptr [ebp+8]  
0041EAF4 C7 00 03 00 00 00    mov         dword ptr [eax],3  
0041EAFA 8B 45 0C             mov         eax,dword ptr [ebp+0Ch]  
0041EAFD C7 00 03 00 00 00    mov         dword ptr [eax],3 

观察汇编代码发现传递的不再是复制体,而是这个局部变量的内存地址。

ebp+8和ebp+0xch保存的是传递的参数,而这两个地址保存了外部代码中局部变量的地址,通过修改这两个地址上保存的值就可以影响到外部变量的值。

但是这样写有一个弊端就是,函数可以随意修改参数使其指向新的地址,这个地址可能会造成无法访问05错误,那有没有办法使得参数只能保存传递过来的变量的地址?而不能修改成其他地址,这样就只能修改这个地址的值,用户也无法修改这个参数使其指向新的地址。

引用

引用可以看作是一个变量,他存储传递过来参数的地址且只保存这个地址,无法在保存其他地址。

void Test(int& x, int& y)
{
	x = 3;
	y = 3;

	x = *(int*)124;
}

int main()
{
	int x = 2;
	int y = 2;
	Test(x, y);
	cout << x << " " << y << endl;
	return 0;
}

通过这种方式去修改传递过来的参数的值,但是如果想让x重新指向一个地址,那么就会发生0xC0000005错误。

041333D 8D 45 E8              lea         eax,[ebp-18h]  
00413340 50                   push        eax  
00413341 8D 4D F4             lea         ecx,[ebp-0Ch]  
00413344 51                   push        ecx  
00413345 E8 56 E2 FF FF       call        004115A0

Test:
0041EAF1 8B 45 08             mov         eax,dword ptr [ebp+8]  
0041EAF4 C7 00 03 00 00 00    mov         dword ptr [eax],3  
0041EAFA 8B 45 0C             mov         eax,dword ptr [ebp+0Ch]  
0041EAFD C7 00 03 00 00 00    mov         dword ptr [eax],3  
0041EB03 8B 45 08             mov         eax,dword ptr [ebp+8]  
0041EB06 8B 0D 7C 00 00 00    mov         ecx,dword ptr ds:[0000007Ch]  
0041EB0C 89 08                mov         dword ptr [eax],ecx  

参数的传递和指针是一样的,没有什么区别。

通过汇编代码观察红色指令,想要从0000007Ch地址中取到数据存储到ecx,0000007Ch显然是不可

访问地址,如果是一个可访问地址,那么也仅仅是给参数赋值罢了,并不会修改参数使其指向新的地址

在使用引用数据类型的时候,

int s = 333;
x = *(int*)&s;

这种操作也仅仅是给x重新赋值罢了

void TestStudent(Student& st)
{
	Student stu;
	st = stu;
	cout << st.x << " " << st.y << endl;

}
	Student stu{ 3,3 };
	TestStudent(stu);
	cout << stu.x <<  " " << stu.y << endl;

虽然给TestStudent传递了一个参数并调用构造函数赋值为3,3

但是在函数内部中又创建了一个Student类型赋值给了st,这样打印出的结果是1,1

在main函数中打印的结果也会是1,1。

1、引用类型是C++里面的类型

2、引用类型只能赋值一次,不能重新赋值

3、引用只是变量的一个别名.

4、引用可以理解成是编译器维护的一个指针,但并不占用空间(如何去理解这句话?).

5、使用引用可以像指针那样去访问、修改对象的内容,但更加安全.

友元

友元?

友元函数,友元类。

友元可以看作是一个类或结构体的朋友。

他可以访问类或者结构体中的私有变量或者私有函数。

class Student
{
public:
	Student();
	Student(int x, int y);
	~Student();

public:
	int x;
	int y;

	friend void print(Student& student);
	
};

void print(Student& student)
{
	cout << student.x << " " << student.y << endl;
}

Student stu{ 3,3 };
print(stu);

当类的成员变量是公共的时候友元函数并没有起到太大的作用,只要在成员变量是私有的时候友元函数才起作用。

友元函数可以访问类的私有变量或者私有方法,但是友元函数不会被继承。

class Student
{
public:
	Student();
	Student(int x, int y);

private:
	int x;
	int y;

	 friend void print(Student& student);
	 friend class Persone; // 在这里声明友元类
};

class Persone
{
public:
	Persone();
	void print(Student& stud);
private:
	int x;
};

// 因为Persone是友元类所以print可以访问Student的私有变量。
void Persone::print(Student& stud)
{
	cout << hex << stud.x << " " << stud.y << endl;
}

Persone::Persone()
{
	this->x = 10;
}

友元函数不是成员函数没有this指针,在类中声明友元函数后在外部实现的时候不需要类名::函数名这样指定。

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

(1) 运算符重载的某些场合需要使用友元.

(2) 两个类要共享数据的时候.

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

(1) 成员函数有this指针,而友元函数没有this指针

(2) 友元函数是不能被继承的,就像父亲的朋友未必是儿子的朋友

运算符重载

运算符重载,在一些比较规则中现有的比较规则不足以满足条件需求,必须比较两个类的大小?

使用语言提供的比较符号显然是不可能比较出来的,因为比较符号不知如何去比较两个类的大小,依据是什么?

class Student
{
public:
	Student();
	Student(int x);

	friend void print(Student& student);
	friend class Persone;

	friend Student operator+(const Student& _Left, const Student& _Right);
	friend Student operator-(const Student& _Left, const Student& _Right);
	friend Student operator*(const Student& _Left, const Student& _Right);
	friend Student operator/(const Student& _Left, const Student& _Right);

	friend bool operator>(const Student _Left, const Student& _Right);
	friend bool operator<(const Student _Left, const Student& _Right);
	friend bool operator>=(const Student _Left, const Student& _Right);
	friend bool operator<=(const Student _Left, const Student& _Right);

	// 输出函数,方便打印结果
	friend ostream& operator<<(ostream& os, const Student& num);

private:
	 int x;
};

Student operator+(const Student& _Left, const Student& _Right)
{
	return Student(_Left.x + _Right.x);
}
Student operator-(const Student& _Left, const Student& _Right)
{
	return Student(_Left.x - _Right.x);
}
Student operator*(const Student& _Left, const Student& _Right)
{
	return Student(_Left.x * _Right.x);
}
Student operator/(const Student& _Left, const Student& _Right)
{
	if (_Right.x == 0)
	{
		throw runtime_error("除数不能为0");
	}
	return Student(_Left.x / _Right.x);
}

bool operator>(const Student _Left, const Student& _Right)
{
	return _Left.x > _Right.x;
}

bool operator<(const Student _Left, const Student& _Right)
{
	return _Left.x < _Right.x;
}

bool operator>=(const Student _Left, const Student& _Right)
{
	return _Left.x >= _Right.x;
}

bool operator<=(const Student _Left, const Student& _Right)
{
	return _Left.x <= _Right.x;
}

// 输出友元函数重载
ostream& operator<<(ostream& os, const Student& num) {
	os << num.x;
	return os;
}

运算符重载什么情况下一定要使用友元函数?

当左操作数不是类的对象的时候,需要访问类的私有变量的时候,必须要用到友元函数来实现。

从汇编的角度看引用和指针的区别

// 引用
00007FF679F05C1B 48 8D 55 24          lea         rdx,[y]  
00007FF679F05C1F 48 8D 4D 04          lea         rcx,[x]  
00007FF679F05C23 E8 A3 B6 FF FF       call        Test (07FF679F012CBh)
// Test:
00007FF679F03C24 48 8B 85 E0 00 00 00 mov         rax,qword ptr [x]  
00007FF679F03C2B C7 00 03 00 00 00    mov         dword ptr [rax],3  
00007FF679F03C31 48 8B 85 E8 00 00 00 mov         rax,qword ptr [y]  
00007FF679F03C38 C7 00 03 00 00 00    mov         dword ptr [rax],3  

// 指针
00007FF679F05C63 48 8D 55 24          lea         rdx,[y]  
00007FF679F05C67 48 8D 4D 04          lea         rcx,[x]  
00007FF679F05C6B E8 EE B9 FF FF       call        Test2 (07FF679F0165Eh)
// Test2:
00007FF679F0F3E4 48 8B 85 E0 00 00 00 mov         rax,qword ptr [x]  
00007FF679F0F3EB C7 00 03 00 00 00    mov         dword ptr [rax],3  
00007FF679F0F3F1 48 8B 85 E8 00 00 00 mov         rax,qword ptr [y]  
00007FF679F0F3F8 C7 00 03 00 00 00    mov         dword ptr [rax],3 

没啥区别,单从汇编的角度分析看的话,肯本不知道是使用了指针还是引用。

License:  CC BY 4.0