北京大学MOOC C++学习笔记(三)运算符重载
运算符重载:
运算符重载,就是对已有的运算符(C++中预定义的运算符)赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为。
运算符重载的目的是:扩展C++中提供的运算符的适用范围,使之能作用于对象。
同一个运算符,对不同类型的操作数,所发生的行为不同。
- complex_a + complex_b 生成新的复数对象
- 5 + 4 = 9
运算符重载的形式:
- 运算符重载的实质是函数重载
- 可以重载为普通函数,也可以重载为成员函数
- 把含运算符的表达式转换成对运算符函数的调用。
- 把运算符的操作数转换成运算符函数的参数。
- 运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。
运算符重载的形式:
返回值类型 operator 运算符(形参表)
{
……
}
运算符重载示例:
class Complex
{
public:
double real,imag;
Complex( double r = 0.0, double i= 0.0 ):real(r),imag(i) { }
Complex operator-(const Complex & c);
};
Complex operator+( const Complex & a, const Complex & b)
{
return Complex( a.real+b.real,a.imag+b.imag); // 返回一个临时对象
}
Complex Complex::operator-(const Complex & c)
{
return Complex(real - c.real, imag - c.imag); // 返回一个临时对象
}
int main()
{
Complex a(4,4),b(1,1),c;
c = a + b; // 等价于c=operator+(a,b);
cout << c.real << "," << c.imag << endl;
cout << (a-b).real << "," << (a-b).imag << endl;
//a-b 等价于a.operator-(b)
return 0;
}
输出:
5,5
3,3
c = a + b; 等价于c=operator+(a,b);
a-b 等价于a.operator-(b)。
重载为成员函数时 , 参数个数为运算符目数减一 。
重载为普通函数时 , 参数个数为运算符目数 。
赋值运算符 ‘=’重载:
有时候希望赋值运算符两边的类型可以不匹配,比如,把一个int类型变量赋值给一个Complex对象,或把一个 char * 类型的字符串赋值给一个字符串对象,此时就需要重载赋值运算符“=”。
赋值运算符“=”只能重载为成员函数。
class String {
private:
char * str;
public:
String ():str(new char[1]) { str[0] = 0;}
const char * c_str() { return str; };
String & operator = (const char * s);
String::~String( ) { delete [] str; }
};
String & String::operator = (const char * s)
{ // 重载“=”得 以使得 obj = “hello” 能够成立
delete [] str;
str = new char[strlen(s)+1];
strcpy( str, s);
return * this;
}
int main()
{
String s;
s = "Good Luck," ; //于 等价于 s.operator=("Good Luck,");
cout << s.c_str() << endl;
// String s2 = "hello!"; // 这条语句要是不注释掉就会出错
s = "Shenzhou 8!"; //于 等价于 s.operator=("Shenzhou 8!");
cout << s.c_str() << endl;
return 0;
}
输出:
Good Luck,
Shenzhou 8!
运算符重载为友元函数:
一般情况下,将运算符重载为类的成员函数,是较好的选择。
但有时,重载为成员函数不能满足使用要求,重载为普通函数,又不能访问类的私有成员,所以需要将运算符重载为友元。
class Complex
{
double real,imag;
public:
Complex( double r, double i):real(r),imag(i){ };
Complex operator+( double r );
friend Complex operator + (double r,const Complex & c);
};
运算符重载实例:可变长整型数组
#include<iostream>
#include<memory.h>
using namespace std;
class CArray {
int size; //数组元素的个数
int *ptr; //指向动态分配的数组
public:
CArray(int s = 0); //s代表数组元素的个数
CArray(CArray & a);
~CArray();
void push_back(int v); //用于在数组尾部添加一个元素v
CArray & operator=( const CArray & a);//用于数组对象间的赋值
int length() { return size; } //返回数组元素个数
int & operator[](int i) //返回值为 int 不行!不支持 a[i] = 4
{//用以支持根据下标访问数组元素,
// 如n = a[i] 和a[i] = 4; 这样的语句
return ptr[i];
}
};
CArray::CArray(int s):size(s)
{
if( s == 0)
ptr = NULL;
else
ptr = new int[s];
}
CArray::CArray(CArray & a) {
if( !a.ptr) {
ptr = NULL;
size = 0;
return;
}
ptr = new int[a.size];
memcpy( ptr, a.ptr, sizeof(int ) * a.size);
size = a.size;
}
CArray::~CArray()
{
if( ptr)
delete [] ptr;
}
CArray & CArray::operator=( const CArray & a)
{ // 赋值号的作用是使“=” 左边对象里存放的数组,大小和内容都和右边的对象一样
if( ptr == a.ptr) // 防止a=a 这样的赋值导致出错
return * this;
if( a.ptr == NULL) { // 如果a 里面的数组是空的
if( ptr )
delete [] ptr;
ptr = NULL;
size = 0;
return * this;
}if( size < a.size) { // 如果原有空间够大,就不用分配新的空间
if(ptr)
delete [] ptr;
ptr = new int[a.size];
}
memcpy( ptr,a.ptr,sizeof(int)*a.size);
size = a.size;
return * this;
} // CArray & CArray::operator=( const CArray & a)
void CArray::push_back(int v)
{ // 在数组尾部添加一个元素
if( ptr) {
int * tmpPtr = new int[size+1]; // 重新分配空间
memcpy(tmpPtr,ptr,sizeof(int)*size); // 拷贝原数组内容
delete [] ptr;
ptr = tmpPtr;
}
else // 数组本来是空的
ptr = new int[1];
ptr[size++] = v; // 加入新的数组元素
}
int main() { //要编写可变长整型数组类,使之能如下使用:
CArray a; //开始里的数组是空的
for( int i = 0;i < 5;++i)
a.push_back(i);
CArray a2,a3;
a2 = a;
for( int i = 0; i < a.length(); ++i )
cout << a2[i] << " " ;
a2 = a3; //a2是空的
for( int i = 0; i < a2.length(); ++i ) //a2.length()返回0
cout << a2[i] << " ";
cout << endl;
a[3] = 100;
CArray a4(a);
for( int i = 0; i < a4.length(); ++i )
cout << a4[i] << " ";
return 0;
}
流插入运算符和流提取运算符的重载:
cout 是在 iostream 中定义的,ostream 类的对象。
“<<” 能用在cout 上是因为,在iostream里对 “<<” 进行了重载。
代码示例:
假定c是Complex复数类的对象,现在希望写“cout << c;”,就能以“a+bi”的形式输出c的值,写“cin>>c;”,就能从键盘接受“a+bi”形式的输入,并且使得c.real = a,c.imag = b。
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
class Complex {
double real,imag;
public:
Complex( double r=0, double i=0):real(r),imag(i){ };
friend ostream & operator<<( ostream & os,
const Complex & c);
friend istream & operator>>( istream & is,Complex & c);
};
ostream & operator<<( ostream & os,const Complex & c)
{
os << c.real << "+" << c.imag << "i"; // 以"a+bi" 的形式输出
return os;
}
istream & operator>>( istream & is,Complex & c)
{
string s;
is >> s; // 将"a+bi" 作为字符串读入, “a+bi” 中间不能有空格
int pos = s.find("+",0);
string sTmp = s.substr(0,pos); // 分离出代表实部的字符串
c.real = atof(sTmp.c_str()); //atof 库函数能将const char* 指针成 指向的内容转换成 float
sTmp = s.substr(pos+1, s.length()-pos-2); // 分离出代表虚部的字符串
c.imag = atof(sTmp.c_str());
return is;
}
int main()
{
Complex c;
int n;
cin >> c >> n;
cout << c << "," << n;
return 0;
}
类型转换运算符和自增、自减运算符的重载
#include <iostream>
using namespace std;
class Complex
{
double real,imag;
public:
Complex(double r=0,double i=0):real(r),imag(i) { };
operator double () { return real; }
//符 重载强制类型转换运算符 double
};
int main()
{
Complex c(1.2,3.4);
cout << (double)c << endl; //出 输出 1.2
double n = 2 + c; //于 等价于 double n=2+c.operator double()
cout << n; //出 输出 3.2
}
自增运算符++、自减运算符--有前置/后置之分,为了区分所重载的是前置运算符还是后置运算符,C++规定:
前置运算符作为一元运算符重载
重载为成员函数:
T & operator++();
T & operator--();
重载为全局函数:
T1 & operator++(T2);
T1 & operator—(T2);
后置运算符作为二元运算符重载,多写一个没用的参数:
重载为成员函数:
T operator++(int);
T operator--(int);
重载为全局函数:
T1 operator++(T2,int );
T1 operator—( T2,int);
#include <iostream>
using namespace std;
class CDemo {
private :
int n;
public:
CDemo(int i=0):n(i) { }
CDemo & operator++(); // 用于前置形式
CDemo operator++( int ); // 用于后置形式
operator int ( ) { return n; }
friend CDemo & operator--(CDemo & );
friend CDemo operator--(CDemo & ,int);
};
CDemo & CDemo::operator++()
{ //置 前置 ++
n ++;
return * this;
}
CDemo CDemo::operator++( int k )
{ //置 后置 ++
CDemo tmp(*this); // 记录修改前的对象
n ++;
return tmp; // 返回修改前的对象
} // s++即为: s.operator++(0);
CDemo & operator--(CDemo & d)
{// 前置--
d.n--;
return d;
} //--s即为: operator--(s);
CDemo operator--(CDemo & d,int)
{// 后置--
CDemo tmp(d);
d.n --;
return tmp;
} //s--即为: operator--(s, 0);
int main()
{
CDemo d(5);
cout << (d++ ) << ","; //于 等价于 d.operator++(0);
cout << d << ",";
cout << (++d) << ","; //于 等价于 d.operator++();
cout << d << endl;
cout << (d-- ) << ","; //于 等价于 operator--(d,0);
cout << d << ",";
cout << (--d) << ","; //于 等价于 operator--(d);
cout << d << endl;
return 0;
}
operator int ( ) { return n; }
这里,int 作为一个类型强制转换运算符被重载, 此后
Demo s;
(int) s ; //于 等效于 s.int();类型强制转换运算符被重载时不能写返回值类型,实际上其返回值类型就是该类型强制转换运算符代表的类型
运算符重载的注意事项:
1. C++不允许定义新的运算符 ;
2. 重载后运算符的含义应该符合日常习惯;
complex_a + complex_b
word_a > word_b
date_b = date_a + n
3. 运算符重载不改变运算符的优先级;
4. 以下运算符不能被重载:“.”、“.*”、“::”、“?:”、sizeof;
5. 重载运算符()、[]、->或者赋值运算符=时,运算符重载函数必须声明为
类的成员函数。
上一篇: Java基础教程之对象引用