文章目录
- 一、运算符重载的关键字和注意点
- 二、重载`=` 运算符
- 三、重载`+` 运算符
- 四、重载 `==` 运算符
- 五、重载`前置++` 和 `后置 ++` 运算符
- 六、重载 `<<` `>>`运算符
一、运算符重载的关键字和注意点
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。函数名字为:关键字
operator
后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
需要注意的点:
(1)不能通过连接其他符号来创建新的操作符:比如
operator@
。(2)重载操作符必须有一个类类型参数。
(3)用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义。
(4)作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
。
(5).* :: sizeof ?: .
注意以上5个运算符不能重载。
(6)可以写在类内作为成员函数也可以在全局函数,有一个例外就是赋值运算符只能作为成员函数。
二、重载=
运算符
赋值运算符重载函数是类的默认成员函数,当我们不写的时候编译器会自动生成,又因为是编译器会自动生成的,如果作为全局函数去写的话会与类内的自动生成的赋值运算符重载冲突,所以赋值运算符只能作为成员函数去写。
返回类型 :
const 类名 &
返回类的话就可以实现连续赋值了。
函数名:operator=
。
参数类型:(const & 类名)
,因为编译器自动会传一个隐含的this
,所以我们传一个参数就够了。
实现:
class Kind
{
public://构造函数Kind(int a = 10,int b = 10){_a = a;_b = b;}//重载 = //只能作为成员函数 const 防止被修改const Kind & operator=(const Kind& p) {this->_a = p._a;this->_b = p._b;//返回 *this 使其可以连续赋值return *this; }
private:int _a;int _b;
};int main()
{Kind p1(20, 20);Kind p2;Kind p3;//连续赋值p3 = p2 = p1;cout << "p1: " << p1._a << " " << p1._b << endl;cout << "p2: " << p2._a << " " << p2._b << endl;cout << "p3: " << p3._a << " " << p3._b << endl;return 0;
}
其实默认的赋值运算符重载函数就是像上写的那样进行赋值,我们对于这样的拷贝叫做浅拷贝,这样做有一个弊端就是如果遇到动态申请的空间的话就有可能发生程序崩溃,这是因为共用一块空间当另一个对象将这块空间释放之后被赋值的那个对象再使用这块空间时就会发生崩溃。
如:
class Kind
{
public://构造函数Kind(int a = 10,int b = 10){_a = a;_b = b;arr = new int;*arr = a; }//重载 = //只能作为成员函数 const 防止被修改const Kind & operator=(const Kind& p) {this->_a = p._a;this->_b = p._b;this->arr = p.arr;//返回 *this 使其可以连续赋值return *this; }private:int _a;int _b;int* arr;
};int main()
{Kind p1(20, 20);Kind p2 = p1;return 0;
}
当上述的p1将arr释放了,p2再使用arr就会发生崩溃。
当我们遇到这种情况时我们使用深拷贝。
//深拷贝
const Kind& operator=(const Kind& p)
{this->_a = p._a;this->_b = p._b;int* tmp = new int;if (tmp == nullptr)exit(-1);*tmp = *p.arr;this->arr = tmp;//返回 *this 使其可以连续赋值return *this;
}
总结:
当遇到动态申请的空间时需要重写赋值运算符,如果没有用编译器自动生成的即可。
三、重载+
运算符
1、作为成员函数
返回值: 类型 (类) ,用传值的方式即可,因为返回值是存在栈中当函数销毁时该返回值也会被系统回收。
函数名:operator+
参数:(const & 类名)
,因为编译器自动会传一个隐含的this
,所以我们传一个参数就够了。
实现:
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}//重载 + //作为成员函数Kind operator+(const Kind & p){Kind tmp;tmp._a = p._a + this->_a;tmp._b = p._b + this->_b;return tmp;}
private:int _a;int _b;
};int main()
{Kind p1(20, 20);Kind p2(20, 20);Kind p3 = p1 + p2;cout << "p1: " << p1._a << " " << p1._b << endl;cout << "p2: " << p2._a << " " << p2._b << endl;cout << "p3: " << p3._a << " " << p3._b << endl;return 0;
}
2、全局函数:
与成员函数不同的是需要多传一个参数,其他的与成员函数一样。
实现:
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}
//作为友元函数,可以访问私有成员。
friend Kind operator+(const Kind& p1 ,const Kind &p2 );private:int _a;int _b;
};//重载 + //全局函数Kind operator+(const Kind& p1 ,const Kind &p2 )
{Kind tmp;tmp._a = p1._a + p2._a;tmp._b = p1._b + p2._b;return tmp;
}
3、总结:
可以写全局的函数也可以写成员函数,实现 - * /
运算符重载时的形式与上述的一样。
四、重载 ==
运算符
1、作为成员函数
返回值:
bool
。
函数名:operator==
。
参数:(const &类名)
,因为编译器自动会传一个隐含的this
,所以我们传一个参数就够了。
实现:
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}//重载 == //作为成员函数bool operator==(const Kind& p){//成员都相等就相等if (this->_a == p._a && this->_b == p._b)return true;elsereturn false;}
private:int _a;int _b;
};int main()
{Kind p1(20, 20);Kind p2(20, 20);if (p1 == p2){cout << "p1==p2" << endl;}else{cout << "p1!=p2" << endl;}return 0;
}
2、作为全局函数
与成员函数不同的是需要多传一个参数,其他的与成员函数一样。
实现:
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}
//作为友元函数,可以访问私有成员。
friend Kind operator==(const Kind& p1 ,const Kind &p2 );private:int _a;int _b;
};//重载 == //作为全局函数
bool operator==(const Kind& p1, const Kind& p2)
{if (p1._a == p2._a && p1._b == p2._b)return true;elsereturn false;
}
3、总结
可以写全局的函数也可以写成员函数,实现 != >= <= > <
运算符重载时的形式与上述的一样。
五、重载前置++
和 后置 ++
运算符
1、前置++
(1)作为成员函数
返回值:返回用引用的方式返回
*this
,这样即可进行计算和Kind p2 = ++p1;
和提高效率。
函数名:operator++
。
参数:无(实际上有一个隐含的this指针)。
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}//重载 前++ //作为成员函数Kind & operator++(){//对成员变量++this->_a++;this->_b++;return *this;}
private:int _a;int _b;
};int main()
{Kind p1(20, 20);cout << p1._a << " " << p1._b << endl;++p1;cout << p1._a << " " << p1._b << endl;return 0;
}
(2)作为全局函数
多了个参数,其余的一样。
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}
//作为友元函数,可以访问私有成员。
friend Kind& operator++(Kind& p);private:int _a;int _b;
};
//重载 前置++ //作为全局函数
Kind& operator++(Kind& p)
{//对成员变量++p._a++;p._b++;return p;
}
2、后置++
后置++与前置++的不同是我们在加之前要保存原来的作为返回值,这样才符合后置++运算。
后置++与前置++如何区别捏?
后置++比前置++的参数多一个 int ,这个参数我们不用传值,主要用于区分前置和后置。
(1)作为成员函数
返回值:返回
临时的原来的类
。
函数名:operator++
。
参数:(int)(实际上还有一个隐含的this指针)。
实现:
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}//重载 后置++ //作为成员函数Kind operator++(int){//对成员变量++Kind p = *this;this->_a++;this->_b++;return p;}
private:int _a;int _b;
};
int main()
{Kind p1(20, 20);cout << p1._a << " " << p1._b << endl;Kind p2 = p1++;cout << p2._a << " " << p2._b << endl;cout << p1._a << " " << p1._b << endl;return 0;
}
(2)作为全局函数
多了个参数,其余的一样。
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}//作为友元函数,可以访问私有成员。
friend Kind operator++(Kind & tmp,int);
private:int _a;int _b;
};
//重载 后置++ //作为全局函数
Kind operator++(Kind & tmp,int)
{//对成员变量++Kind p = tmp;tmp._a++;tmp._b++;return p;
}
3、总结
前置和后置的区别在于返回值,他们也可实现全局和成员函数两种,前置 – 和 后置 – 也是类似这样写。
六、重载 <<
>>
运算符
cout
是 ostream
类的一个对象,而 <<
符在这个类已经进行了一些重载
如:
ostream& operator<<(const int &a)
{//输出代码return *this;
}
因为在参数列表中会隐含着一个 ostream *this
指针,所以如果我们在其他类将其作为成员函数去重载的话也会传一个隐含的 this
指针,这样的话第一个参数就会与 cout
冲突,所以为了避免冲突我们只能将重载 <<
函数作为全局函数。
重载 >>
返回类型 :
ostream&
使其能够连续的输出,&
提高效率。
函数名:operator<<
。
参数:ostream & ost, const Kind& p
。
//输出ostream & operator<<( ostream& ost, const Kind& p)
{ost << p._a << " " << p._b << endl;return ost;
}
一样的 cin
是 istream
类 的对象,>>
也在 istream
类里进行了一些重载。
所以只能在全局里进行重载,原因同上。
重载 >>
返回类型 :
istream&
使其能够连续的输出。
函数名:istream<<
。
参数:istream & ost, const Kind& p
。
//输入
istream& operator>>(istream& ist, Kind& p)
{ist >> p._a >> p._b;return ist;
}
测试:
//重载 >> 和 <<
class Kind
{
public://构造函数Kind(int a = 10, int b = 10){_a = a;_b = b;}//作为友元函数,可以访问私有成员。 friend istream& operator>>(istream& ist, const Kind& tmp);friend ostream& operator<<(ostream& ost, const Kind& p);
private:int _a;int _b;
};//输出ostream & operator<<( ostream& ost, const Kind& p)
{ost << p._a << " " << p._b << endl;return ost;
}//输入
istream& operator>>(istream& ist, Kind& p)
{ist >> p._a >> p._b;return ist;
}
int main()
{Kind p1;//输入cin >> p1;//输出cout << p1;return 0;
}