1、函数符号为什么要重载?
答:运算符重载就是赋予运算符多重含义。C++通过重新定义运算符,使它能够用于特定类的对象执行特定的操作(不然只能用于对基本类型的常量或变量之间的运算,而不能用于对象之间的运算),这便增强了C++语言的扩充能力。
2、哪些运算符需要重载,哪些不需要重载?
答:可以重载的运算符:
- 算术运算符:+,-,*,/,%,++,–;
- 位操作运算符:&,|,~,^,<<,>>
- 逻辑运算符:!,&&,||;
- 比较运算符:<,>,>=,<=,==,!=;
- 赋值运算符:=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=;
- 其他运算符:[],(),->,(逗号运算符),new,delete,new[],delete[],->*。
不能重载的运算符:
- ::(作用域)
- .(成员运算符)
- .*成员指针解引用
- Sizeof(类型大小)
- ? :三元运算符
- Typeid 获取类型的信息(返回值类型typeinfo)
只能对已有的运算符进行重载,不能发明新的运算符;
不能对基本类型进行运算符重载(运算符重载中至少有一个类型是非基本类型);
不能改变运算符的运算特性;不能把一元的改成二元的;
3、为什么C语言不需要重载,而C++运算符需要重载?
答:这个本小白也没有找到具体解释,小白自己理解的为:C++是面向对象的语言,即不止需要对基本类型的常量和变量进行操作,还需要对对象进行操作,这就需要对基本类型的运算符赋予新的运算意义。而C语言中止运算符操作的对象也都是基本类型的常量或者变量,不需要赋予新的运算意义。
‘+’运算符进行重载
class Complex
{
public:
Complex(int a, int b)
{
cout << "Complex(int a, int b)" << endl;
_a = a;
_b = b;
}
Complex operator+(const Complex& src)const
{
cout << "Complex operator+(const Complex& src)const"<<endl;
int a = _a + src._a;
int b = _b + src._b;
return Complex(a, b);
}
}
int main()
{
Complex c1(1, 2);
Complex c2(2, 3);
Complex c3 = c1 + c2;
}
图解:
‘-’ ‘||’ ‘&&’运算符重载及调用方法与上类似,这里只给出代码:
Complex operator-(const Complex& src)const
{
int a = _a - src._a;
int b = _b - src._b;
return Complex(a, b);
}
bool operator &&(const Complex& src)const
{
return _a && src._a;
}
bool operator ||(const Complex& src)const
{
return _a|| src._a;
}
下边我们来看一下前置加减和后置加减,解释都在代码注释中:
Complex& operator++ ()//前置++,加了之后再用
{
++_a;
return *this;//这里是直接对this本身进行操作,没有额外的临时对象,所以可以返回引用
}
Complex operator ++(int)//后置++,先传值,再++(用了之后再加)
{
int tmp = _a;//记录++之前的值,我们要使用的就是这个临时量
_a++;
return Complex(tmp, _b);//此处不能返回引用,因为构造的是临时对象,出了函数之后就相当于野指针了
}
Complex& operator-- ()//前置--
{
--_a;
return *this;
}
Complex operator --(int)//后置--,先传值,再--
{
int tmp = _a;//记录--之前的值
_a--;
return Complex(tmp, _b);//此处不能返回引用,因为构造的是临时对象,出了函数之后就相当于野指针了
}
这里着重介绍输出和输入运算符重载:
先给代码
class Complex
{
public:
friend ostream& operator <<(ostream& out, const Complex& src);
friend istream& operator >>(istream& is, Complex& p);
}
ostream& operator <<(ostream& out, const Complex& src)//输出运算符重载
{
out << src._a << "+" << src._b << "i" << endl;
return out;
}
istream& operator >>(istream& in, Complex& p)//输入运算符重载
{
in >> p._a;
in >> p._b;
return in;
}
输出和输入运算符为什么必须在类内定义,在类外实现?
我们先在类内实现来看一下:
class Complex
{
public:
void operator<<(/*this*/ostream&out)
{
out<<_a<<"+"<<_b<<"i"<<endl;
}
}
int main()
{
Complex c1(1, 2);
c1.operator<<(/*this*/cout);
}
注意:<<是个一个双目运算符,即需要两个参数,在类内实现的话,第一个参数是this指针,这是默认的位置。不可改变,输出流在第二个参数,那么在类外使用时,就必须按照“ c1.operator<<(/*this*/cout);
”这种方式来调用,为的就是把输出流放在第二位。
我们发现,这样调用的话非常不方便,如何才能像‘+’‘-’这样方便的使用呢?
要解决这个问题,我们首先要做的就是调换这两个参数的位置(因为类似cout<<c1这种输出,输出流都是在第一位的),但是这在类内是不可能实现的,因为系统默认this指针在第一位,不能调换,此时就必须在类外去实现这个操作。也就是这种:
ostream& operator <<(ostream& out, const Complex& src)//输出运算符重载
{
out << src._a << "+" << src._b << "i" << endl;
return out;
}
此时又有一个问题,为什么返回值必须是ostream&
而不能是void
呢?
这是因为我们要实现运算符的连续输出,如果是void类:cout<<c1
这是没有问题的,可以输出,但是cout<<c1<<endl;
就会报错,不能连续输出。之前说过<<是一个双目运算符,那么它就需要两个参数,如果连续输出的话,后边输出的第一个参数就是前边的返回值,如果返回值为空,那么就缺少第一个参数,自然就不能连续输出,所以返回值类型必须为ostream&
型,保留第一个参数。
总代码实现:
class Complex
{
public:
Complex(int a, int b)
{
cout << "Complex(int a, int b)" << endl;
_a = a;
_b = b;
}
Complex operator+(const Complex& src)const
{
cout << "Complex operator+(const Complex& src)const"<<endl;
int a = _a + src._a;
int b = _b + src._b;
return Complex(a, b);
}
Complex operator-(const Complex& src)const
{
int a = _a - src._a;
int b = _b - src._b;
return Complex(a, b);
}
bool operator &&(const Complex& src)const
{
return _a && src._a;
}
bool operator ||(const Complex& src)const
{
return _a|| src._a;
}
void operator<<(/*this*/ostream& out)//第一个是this指针,当前对象,第二个是输出流
{
out << _a << "+" << _b << "i" << endl;
}
Complex& operator++ ()//前置++
{
++_a;
return *this;
}
Complex operator ++(int)//后置++,先传值,再++
{
int tmp = _a;//记录++之前的值
_a++;
return Complex(tmp, _b);//此处不能返回引用,因为构造的是临时对象,出了函数之后就相当于野指针了
}
Complex& operator-- ()//前置--
{
--_a;
return *this;
}
Complex operator --(int)//后置--,先传值,再--
{
int tmp = _a;//记录--之前的值
_a--;
return Complex(tmp, _b);//此处不能返回引用,因为构造的是临时对象,出了函数之后就相当于野指针了
}
//friend void operator <<(ostream& out, const Complex& src);//这种没有返回值时,只能输出一个,如果连续输出,第二个要输出的就少了第一个参数
friend ostream& operator <<(ostream& out, const Complex& src);
friend istream& operator >>(istream& is, Complex& p);
private:
int _a;//实部
int _b;//虚部
};
//void operator <<(ostream& out, const Complex& src)
ostream& operator <<(ostream& out, const Complex& src)//输出运算符重载
{
out << src._a << "+" << src._b << "i" << endl;
return out;
}
istream& operator >>(istream& is, Complex& p)//输入运算符重载
{
is >> p._a;
is >> p._b;
return is;
}
int main()
{
string s1 = "aaaa";
string s2 = "bbbbb";
string s3 = s1 + s2;
cout << s3 << endl;
Complex c1(1, 2);
Complex c2(2, 3);
Complex c3 = c1 + c2;
cout << c1 << c2 << endl;
cout << c3;
cin >> c1;
if (c1 && c2)
{
cout << "all 0" << endl;
}
c1.operator<<(/*this*/cout);
cout << c1<<c2;
return 0;
}
附加内容:
在函数重载时,一般可以写成成员函数和全局函数两种,一般也都写成成员函数,这是因为编译器在解析到重载运算符的时候,会先去类的成员函数中找重载的成员函数,如果没有再去全局区找重载的全局函数。所以能写成成员函数就尽量写成成员函数。
附加内容参考:https://blog.csdn.net/uestclr/article/details/51126112.