滴水-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
没啥区别,单从汇编的角度分析看的话,肯本不知道是使用了指针还是引用。