1.基本概念
当运算符被用于类类型对象时,C++语言允许我们为其指定新的含义,同时,我们也可以自定义类类型之间的转换规则。
以下是运算符重载时需要注意的几个原则:
- 当运算符作用于内置类型对象时,我们无法改变该运算符的含义
- 我们只能重载已存在的运算符,不能发明新的运算符
- 重载的运算符的优先级和结合律与对应的内置运算符保持一致
- 以下运算符不能被重载:
:: .* . ? :
- 应使用与内置类型一致的含义
关于选择重载运算符函数作为成员还是非成员函数:
- 赋值(=)、下标( [ ] )、调用( ( ) )和成员访问箭头( -> )必须是成员
- 复合赋值运算符(+=之类)一般来说应该是成员
- 改变对象状态的运算符或者与给定类型密切相关的运算符,入递增、递减和解引用运算符,通常应该是成员
- 具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,通常应是非成员函数
下面以String为例演示重载各个运算符的用法。
2.输入和输出运算符
//输入输出函数只能是非成员函数,故要在类内声明为友元函数
friend std::ostream &operator<<(std::ostream &os, const String &s);
friend std::istream &operator>>(std::istream &is, String &s);
/*输出函数较为简单,需要注意的是,第一个形参要设置为非常量,
因为向流中写入会改变流的状态。返回值是形参中的ostream对象,
原因是要实现连续输出。*/
std::ostream &operator<<(std::ostream &os, const String &s){
os << s._pstr;
return os;
}
/*输入函数较为复杂,因为输入时无法确定输入的字符串长度,
所以此处使用了vector存储为字符数组,再根据字符数组的长度
开辟堆空间存储字符串*/
std::istream &operator>>(std::istream &is, String &s){
/*由于输入长度未知,故需要用vector存储*/
delete [] s._pstr;
std::vector<char> newString;
char ch;
while( (ch = is.get()) != '\n' ){
newString.push_back(ch);
}
/*执行输入运算符时可能会发生错误,故需要检查是否输入正确,
若不正确,则vector中没有数据,不能将vector中的数据复制到string中*/
if(is)
strcpy(s._pstr, &newString[0]);
/*若发生错误前对string对象的内容有改动,则需要将对象置为合法状态
,具体见《C++ Primer》14.2.2节。
else
s = String();
*/
return is;
}
3.算术和关系运算符