C++运算符重载三种形式(成员函数,友元函数,普通函数)详
解
三种重载⽅式
⾸先,介绍三种重载⽅式:
1//作为成员函数重载(常见)
2class Person{
3 Private:
4string name;
5int age;
6public:
7 Person(const char* name, int age):name(name),age(age){}
8bool operator<(const Person& b);
9
10 };
11bool Person::operator<(const Person& b)
12 {
13//作为成员函数时,*this即为左操作数a
14 ...
15 }
1//作为友元函数重载
2class Person{
3private:
4string name;
5int age;
6public:
7 Person(const char* name, int age):name(name),age(age){}
8 friend bool operator<(const Person& a,const Person& b);
9
10 };
11bool operator<(const Person& a,const Person& b)
12 {
13 ...
14 }
1//作为普通函数重载(不推荐)
2class Person{
3public://注意,重载运算符为普通函数时,使⽤到的类成员必须为public
4string name;
5int age;
6public:
7 Person(const char* name, int age):name(name),age(age){}
8
9 };
10bool operator<(const Person& a,const Person& b)
11 {
12 ...
13 }
作为成员函数重载
先介绍第⼀种:
bool Person::operator<(const Person& b),bool是函数返回类型,Person::只是指定了成员函数所属类名。
在作为函数成员重载中,先看下这句话:
单⽬运算符作为类成员函数重载时没有型参(除了后置⾃增(⾃减)有⼀个整型参数:),双⽬运算符作为类成员函数重载时只有⼀个型参,作为运算符的右操作数,其左操作数就是本对象⾃⼰,也就是this。
单⽬运算符⼀般重载为成员函数。
因此在作为成员函数进⾏重载时,是以
1 #include <iostream>
2 #include <cstdlib>
3
4using namespace std;
5
6class A {
7private:
8int a;
9int b;
10public:
11 A(int x = 0, int y = 0):a(x), b(y){}
12 A operator+ (A &C);
13 };
14
15 A A::operator+ (A &C) {
16 A G;
17 G.a = this->a + C.b;
18
19return G;
20 }
21
22int main()
23 {
24 A G(5, 6);
25 A J(7, 8);
26 A K;
27 K = G + J;
28
29return0;
30 }
⽽计算机对于K = G + j;进⾏重载后形式是
K = G.operator+(J);
G为对象,J为参数。
作为友元函数重载
⽽对于第⼆种形式的重载:友元函数(友元函数则是指某些虽然不是类成员却能够访问类的所有成员的函数)进⾏重载,那么它就不存在this指针了,所以需要定义两个参数来运算(对于双⽬运算符),⽽友元函数的实现可以在外⾯定义,但必须在类内部声明。
1 #include <iostream>
2 #include <cstdlib>
3
4using namespace std;
5
6class A {
7private:
8int a;
9int b;
10public:
11 A(int x = 0, int y = 0):a(x), b(y){}
12 friend A operator+ (A &C, A &D);
13 };
14
15 A operator+ (A &C, A &D) {
16 A G;
17 G.a = D.a + C.b;
18
19return G;
20 }
21
22int main()
23 {
24 A G(5, 6);
25 A J(7, 8);
26 A K;
27 K = G + J;
28
29return0;
30 }
推荐类内声明,外部定义,这样不会显得类臃肿。
对于K = G + J;计算机将重载为:
K = operator+(G, J);
声明为友元函数的好处:
1.和普通函数重载相⽐,它能够访问⾮公有成员。
2.将双⽬运算符重载为友元函数,这样就可以使⽤交换律。
弊端:
友元可以像类成员⼀样访问类的成员和函数,但是使⽤不慎会造成破坏类的封装性。
第⼀条没解释的必要,跳过,哈哈哈哈嗝。 =。=
第⼆条:交换律也可以理解成对操作数对称处理。
1 #include <iostream>
2using namespace std;
3
4//复数类
5class Complex{
6public:
7 Complex(): m_real(0.0), m_imag(0.0){ }
8 Complex(double real, double imag): m_real(real), m_imag(imag){ }
9 Complex(double real): m_real(real), m_imag(0.0){ } //转换构造函数
10public:
11 friend Complex operator+(const Complex &c1, const Complex &c2);
12public:
13double real() const{ return m_real; }
14double imag() const{ return m_imag; }
15private:
16double m_real; //实部
17double m_imag; //虚部
18 };
19
20//重载+运算符
21 Complex operator+(const Complex &c1, const Complex &c2){
22 Complex c;
23 c.m_real = c1.m_real + c2.m_real;
24 c.m_imag = c1.m_imag + c2.m_imag;
25return c;
26 }
27
28int main(){
29 Complex c1(25, 35);
30 Complex c2 = c1 + 15.6;
31 Complex c3 = 28.23 + c1;
32 cout<&al()<<" + "<<c2.imag()<<"i"<<endl;
33 cout<&al()<<" + "<<c3.imag()<<"i"<<endl;
34
35return0;
36 }
如果将operator+ 定义为成员函数,根据“+” 运算符具有左结合性”这条原则,Complex c2 = c1 + 15.6;会被转换为下⾯的形式:Complex c2 = c1.operator+(Complex(15.6));
这就是通过对象调⽤成员函数,是正确的。⽽对于Complex c3 = 28.23 + c1;,编译器会尝试转换为不同的形式:
Complex c3 = (28.23).operator+(c1);
很显然这是错误的,因为 double 类型并没有以成员函数的形式重载 +。
也就是说,以成员函数的形式重载 +,只能计算c1 + 15.6,不能计算28.23 + c1,这是不对称的
将22-25⾏代码可替换为
return (c1.real + c2.real, c1.image + c2.image);//调⽤构造函数
将会创建⼀个匿名对象返回。
使⽤&的好处:
将重载的返回类型定义为引⽤类型,能够实现连续输⼊(输出)。
1 #include <iostream>
2 #include <cstdlib>
3
4using namespace std;
5
6class A {
7private:
8int a;
9int b;
10public:
11 A(int x = 0, int y = 0):a(x), b(y){}
12 friend A operator+ (A &C, A &D);
13 friend ostream& operator<<(ostream & out, A &W);
14 };
15
16 A operator+ (A &C, A &D) {
17 A G;
18 G.a = D.a + C.b;
19
20return G;
21 }
22
23 ostream& operator<<(ostream & out, A &W) {
24out << W.a << "" << W.b;
25
26return out;
27 }
28
29int main()
30 {
31 A G(5, 6);
构造函数可以被重载
32 A J(7, 8);
33 A K;
34 K = G + J;
35
36 cout << K << "" << J;
37
38return0;
39 }
将流提取运算符 >> 或流插⼊运算符 << 声明为友元函数,能够访问⾮公有成员。
作为普通函数重载
对于第三种普通函数重载:
因为不属于类了,⾃然也就没⽐较加Person::
要注意的是这种形式⽆法访问⾮公有成员。
其他知识点:
为什么我们要使⽤两个参数(⾮成员函数形式)来重载流提取运算符 >> 和流插⼊运算符 << 呢?
如果我们要⽤成员函数,则会有cout.operator<<(const A& W),但重载双⽬操作符(即为类的成员函数),就只要设置⼀个参数作为右侧运算量,⽽左侧运算量就是对象本⾝,⽽cin和cout并不是对象本⾝(你如果声明为对象,下⾯岂不是this->a + W.b,注意this是类对象的)
如果⼀定要声明为成员函数,只能成为如下的形式:
#include <iostream>
#include <string>
using namespace std;
class A {
public:
A(const string &s = "hello"):str(s){}
ostream& operator<<(ostream &ou) {
ou << this->str;
return ou;
}
private:
string str;
};
int main()
{
A a;
a << cout;//输出"hello"
system("PAUSE");
return0;
}
所以在运⽤这个<<;运算符时就变为这种形式了:
t<<cout;
⽽不是
cout<<t
⽽且也⽆法链式使⽤了
cout<<t<<t<<t<<endl;
对于t << cout 我们可以反向理解cout << t,cout >> t会被重载为cout.operator(t),错误。
这段借⽤⽹上的:
不能重载的根本原因在于,⼤部份的标准库实现中,对ostream,istream类体系采⽤了构造函数保护继承的⽅式。。。致使即使以继承的⽅式来扩展流类,也会在对象实例化时遭遇阻碍。。。另⼀⽅⾯,标准库中的流类,其插⼊符函数没有声明为虚函数,因此⼦类不能对其实现进⾏覆盖,所以也使成员函数重载遭遇到实质的困难。。。总的来说,C++标准I/O库⾮常繁杂且难,其实现思想很多都与常规的OOP有所出⼊。。。在使⽤的时候要谨慎,并最好遵从惯例。。。
⾄于单⽬运算符的声明定义就不多做介绍了,要看的,前⾯脚本之家的链接也有单⽬运算符重载的介绍。
附:今天早上上机时发现VC++6.0不⽀持流提取运算符和流插⼊运算符的友元函数形式重载。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论