导语:
在C++中,运算符重载是一个很基础也很重要的内容,它的知识点很多容易忘,本次在学完C++之后来回顾C++运算符所有相关的知识,相信和第一次接触时的感觉会完全不一样。
运算符重载
C++预定义表示对数据的运算
• +, -,*, /, %, ^, &, ~, !, |, =, <<, >>, != ……
• 只能用于基本的数据类型
• 整型, 实型, 字符型, 逻辑型……
C++提供了数据抽象的手段:用户自己定义数据类型 – 类
调用类的成员函数 ->操作它的对象,这样不能直接对对象进行运算操作,于是,运算符重载就出来了。
运算符重载的好处?
对抽象数据类型也能够直接使用C++提供的运算符
• 程序更简洁
• 代码更容易理解
运算符重载的定义
• 对已有的运算符赋予多重的含义
• 使同一运算符作用于不同类型的数据时->不同类型的行为目的
• 扩展C++中提供的运算符的适用范围, 以用于类所表示的抽象数据类型
• 同一个运算符, 对不同类型的操作数, 所发生的行为不同
运算符重载的实质是函数重载
返回值类型 operator 运算符(形参表)
{
……
}
运算符重载在编译时:
•把含 运算符的表达式->对 运算符函数 的调用
• 把 运算符的操作数 -> 运算符函数的 参数
• 运算符被多次重载时, 根据 实参的类型 决定调用哪个运算符函数
• 运算符可以被重载成普通函数
• 也可以被重载成类的成员函数
运算符可以被重载成普通函数
class Complex {
public:
Complex( double r = 0.0, double i= 0.0 ){
real = r;
imaginary = i;
}
double real; // real part
double imaginary; // imaginary part
};
Complex operator+ (const Complex & a, const Complex & b)
{
return Complex( a.real+b.real, a.imaginary+b.imaginary);
} // “类名(参数表)” 就代表一个对象
Complex a(1,2), b(2,3), c;
c = a + b;
也可以被重载成类的成员函数
注意:重载为成员函数时, 参数个数为运算符目数减一!
class Complex {
public:
Complex( double r= 0.0, double m = 0.0 ):
real(r), imaginary(m) { } // constructor
Complex operator+ ( const Complex & ); // addition
Complex operator- ( const Complex & ); // subtraction
private:
double real; // real part
double imaginary; // imaginary part
};
// Overloaded addition operator
Complex Complex::operator+(const Complex & operand2) {
return Complex( real + operand2.real,
imaginary + operand2.imaginary );
}
// Overloaded subtraction operator
Complex Complex::operator- (const Complex & operand2){
return Complex( real - operand2.real,
imaginary - operand2.imaginary );
}
int main(){
Complex x, y(4.3, 8.2), z(3.3, 1.1);
x = y + z; //x=y.operator + (z)
x = y - z; //x=y.operator - (z)
return 0;
}
赋值运算符的重载
赋值运算符 两边的类型 可以 不匹配
• 把一个 int类型变量 赋值给一个 Complex对象
• 把一个 char * 类型的字符串 赋值给一个 字符串对象
需要 重载赋值运算符 ‘=’
赋值运算符 “=” 只能重载为 成员函数
编写一个长度可变的字符串类String:
• 包含一个char * 类型的成员变量
-> 指向动态分配的存储空间
• 该存储空间用于存放 ‘\0’ 结尾的字符串
class String {
private:
char * str;
public:
String () : str(NULL) { } //构造函数, 初始化str为NULL
const char * c_str() { return str; }
char * operator = (const char * s);
~String( );
};
//重载 ‘=’ obj = “hello”能够成立
char * String::operator = (const char * s){
if(str) delete [] str;
if(s) { //s不为NULL才会执行拷贝
str = new char[strlen(s)+1];
strcpy(str, s);
}
else
str = NULL;
return str;
}
String::~String( ) {
if(str) delete [] str;
};
int main(){
String s;
s = “Good Luck,” ;
cout << s.c_str() << endl;
// String s2 = “hello!”; //这条语句要是不注释掉就会出错
s = "Shenzhou 8!";
cout << s.c_str() << endl;
return 0;
}
赋值运算符重载的意义:浅复制和深复制
浅复制:执行逐个字节的复制工作
深复制:将一个对象中指针变量指向的内容,复制到另一个对象中指针成员对象指向的地方
具体实现:
/*MyString S1, S2;
S1 = “this”;
S2 = “that”;
S1 = S2;*/
String & String::operator = (const String & s){
if(str == s.str) return * this;
if(str) delete [] str
;
if(s.str) { //s.str不为NULL才会执行拷贝
str = new char[strlen(s.str)+1];
strcpy( str,s.str);
}
else
str = NULL;
return * this;
}
对 operator = 返回值类型的讨论
为尽量保留运算符原本的特性和适应a=b=c的特殊情况,我们选择 string &作为返回值的类型。
为 String类编写 复制构造函数时;会面临和 ‘=’ 同样的问题, 用同样的方法处理:
String::String(String & s)
{
if(s.str) {
str = new char[strlen(s.str)+1];
strcpy(str, s.str);
}
else
str = NULL;
}
运算符重载为友元函数
通常, 将运算符重载为类的成员函数
重载为友元函数的情况:
• 成员函数不能满足使用要求
• 普通函数, 又不能访问类的私有成员
解决办法:将运算符+的普通函数重载为友元函数
流插入与流提取运算符重载
自定义输入与输出运算符
ostream & ostream::operator<<(int n)
{
…… //输出n的代码
return * this;
}
ostream & ostream::operator<<( const char * s )
{
…… //输出s的代码
return * this;
}
• 假定下面程序输出为 5hello, 该补写些什么
class CStudent{
public: int nAge;
};
int main(){
CStudent s ;
s.nAge = 5;
cout << s <<"hello";
return 0;
}
/*
ostream & operator<<( ostream & o,const CStudent & s){
o << s.nAge ;
return o;
}
*/
自增自减运算符的重载
自加 ++/自减 – 运算符有 前置/后置 之分
前置运算符作为一元运算符重载
• 重载为成员函数:
T & operator++();
T & operator–();
• 重载为全局函数:
T & operator++(T &);
T & operator—(T &);
++obj, obj.operator++(), operator++(obj) 都调用上述函数
自增自减运算符的重载代码实现
自加 ++/自减 -- 运算符有 前置/后置 之分
前置运算符作为一元运算符重载
• 重载为成员函数:
T & operator++();
T & operator--();
• 重载为全局函数:
T & operator++(T &);
T & operator—(T &);
++obj, obj.operator++(), operator++(obj) 都调用上述函数
CDemo CDemo::operator++(int k) { //后置 ++
CDemo tmp(*this); //记录修改前的对象
n++;
return tmp; //返回修改前的对象
}
CDemo& operator--
(CDemo & d) { //前置--
d.n--;
return d;
}
CDemo operator--
(CDemo & d, int) { //后置--
CDemo tmp(d);
d.n --;
return tmp;
}
运算符重载需要注意的问题
C++不允许定义新的运算符
重载后运算符的含义应该符合日常习惯
• complex_a + complex_b
• word_a > word_b
• date_b = date_a + n
运算符重载不改变运算符的优先级
以下运算符不能被重载: “.”, “.*”, “::”, “?:”,
sizeof重载运算符(), [ ], ->或者赋值运算符=时, 重载函数必须声明为类的成员函数