最近突然开始看C++的重载运算符。想到了一些历史遗留问题。
重载
[]
运算符class safearay { private: int arr[SIZE]; public: safearay() { for(int i = 0; i < SIZE; i++) arr[i] = i; } int& operator[](int i) // 为什么是int & 怎么理解? { return arr[i]; } };
重载
<<
运算符,实现自己的‘cout’。class node { public: const node& operator << (int value) const //这么多const和这里的&是什么意思? { printf("%d",value); return *this; } }myout;
在此先感谢xzy的鼎力相助!
1. 这里的 &
是什么作用?
学习过指针的同学知道,我们定义一个指针ptr,里面需要存储的是一个地址。我们使用的方法是:int a=0, *ptr=&a;
*ptr
表示定义了一个指针变量,&a
表示取变量 a
的地址。比如0x61fe0c2,0x表示十六进制,整体表示一个32位地址。
但是此处的&用法肯定和上面不同:一个operator 能有什么地址?
不要忘记,我们还有一个地方用到了&
。
void swap(int &a,int &b)
{
int tmp;
tmp=a, a=b, b=tmp;
}
int main()
{
int x=1,y=2;
swap(x,y);
}
在这里,我们交换了x
和y
。swap
函数的参数为两个“引用值”。
将变量x
,y
传递至swap
函数后,相当于执行了操作:int &a=x, &b=y;
此处, a
和b
相当于x
和y
的别名。我改变了a
与b
,同时也改变了x
与y
。因而,我们能在函数中实现x
和y
的交换。
如果我们输出a
和x
的地址,我们能发现,他们在内存中的地址是一样的!
也就是说,在内存中,程序不会为a新分配一个空间。
int a=0; int *p = &a; int& b=a;
cout<<&b<<' '<<&a<<' ';
输出:
0x61fe0c 0x61fe0c
2. 如果能理解引用,我们再回过头看重载运算符。
class node{
public:
int arr[10];
int& operator[](int i)
{
...
return arr[i];
}
}X;
int main()
{
int a=X[1]+X[2];
X[1]=4;
return 0;
}
我们理解一下这里main里的语句。
a=x[1]+x[2]
,[]
已经被重载了,所以程序会返回arr[1]
和arr[2]
。a=X.arr[1]+X.arr[2]
,没问题。
x[1]=4;
当我们执行x[1]
这部分时,重载函数就直接返回一个“替身”。这个替身没有名字,替身的主人是X.arr[1]
。
这样一来,等号左边是一个替身,我们让它等于4,就相当于正在操作X.arr[1]=4
。
因此,此处的&
能够让我们方便地使用赋值操作。如果缺失了&
,无法赋值,甚至编译都无法通过。
#include <cstdio>
using namespace std;
class node{
int arr[10];
public:
node()
{
for (int i=0;i<10;i++) arr[i]=i;
}
int& operator[](int i)
{
return arr[i];
}
}X;
int main()
{
int a=X[1]+X[2];
printf("%d,%d,",X[1],a);
X[1]=4;
printf("%d\n",X[1]);
return 0;
}
输出 1,3,4
去掉重载运算符的&
,编译器提示:表达式必须是可修改的左值
3. C++的cin,cout的实现方式?
网上有很多人讨论cin、cout在C++中属于什么成分。它不像函数,用法却神似函数。这里我们自己实现了myout,可以明显地看出,myout是一个“对象”。 就像上面的变量X一样。
class node
{
public:
const node& operator << (int value) const //这么多const和这里的&是什么意思?
{
printf("%d",value);
return *this;
}
const node& operator << (double value) const
{
printf("%lf",value);
return *this;
}
}myout;
通过重载运算符,我们能更方便地输出各种数据类型,而不用写%d
,%lf
等等标识符。例如:myout<<1<<1.5;
我们可以尝试模拟一下这里的过程。
首先,执行myout<<1
。不重载的话,<<
是位运算的意思,即myout*2
。不过我们既然重载了运算符,就按我们写的方式来。
myout<<1
,输出了1,并返回了对象myout
本身。剩下什么?剩下了myout<<1.5
。
形象地讲,(myout<<1)<<1.5 == myout << 1.5;
于是,我们又可以调用一遍重载运算符,继续输出1.5!
那么问题来了:如果我不返回一个“引用”,即const node operator << (int value) const{...}
好像也能实现啊?
问题在于:如果没有引用,每次返回时,程序都会重新为一个新的对象开辟空间。不管是时间还是空间上,都是一种损耗。
当类里面封装的内容比较庞大时,比如高精度,这样子做显然是不合适的。
4. 两个const
是什么成分?
我们知道const int a=0;
此时我们就不能对a
进行赋值。
此处的const
作用相同。为了防止我们对myout中的任何参数进行改变,我们加上了const。
后面的const
则是防止大括号内的*this
被改变。
这就称为“Read Only”.