实质
为运算符定义新的运算功能,使它除具备有原来系统规定的功能外,还具备新定义的功能。运算符重载的实质就是函数重载或函数多态。运算符重载是一种形式的C++多态。目的在于让人能够用同名的函数来完成不同的基本操作。要重载运算符,需要使用被称为运算符函数的特殊函数形式,运算符函数形式:operator p(argument-list)//operator 后面的’p’为要重载的运算符符号。
规则
C++规定重载后的运算符的操作对象必须至少有一个是用户定义的类型(防止二义性)
运算符重载不能改变运算符操作数的个数,优先级和结合性
不能创建一个新的运算符
C++不可以重载的运算符:成员运算符,作用域运算符,条件运算符,sizeof运算符,typeid(一个RTTI运算符),const_cast、dynamic_cast、reinterpret_cast、static_cast强制类型转换运算符
大多数运算符可以通过成员函数和非成员函数进行重载但是下面这四种运算符只能通过成函数进行重载:
= 赋值运算符,()函数调用运算符,[ ]下标运算符,->通过指针访问类成员的运算符。
运算符重载为类的成员函数的一般格式为:
类中:
class X
{
< 函数类型 > operator < 运算符 > ( < 参数表 > )
{
<函数体>
}
}
类外:
<返回数据类型> X::operator <运算符> (<参数表>);
{
<函数体>
}
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。因此:
( 1 ) 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。
( 2 ) 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。
( 3 ) 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。
调用成员函数运算符的格式如下:
< 对象名 > . operator < 运算符 > ( < 参数 > )
它等价于
< 对象名 >< 运算符 >< 参数 >
例如:a + b等价于a. operator + (b)。一般情况下,我们采用运算符的习惯表达方式。
运算符重载为友元函数的一般格式为:
类中:
class X
{
friend < 函数类型 > operator < 运算符 > ( < 参数表 > )
{
<函数体>
}
}
类外:
<返回数据类型> operator <运算符> (<参数表>);
{
<函数体>
}
当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右一一对应。
调用友元函数运算符的格式如下:
operator < 运算符 > ( < 参数1 > , < 参数2 > )
它等价于
< 参数1 >< 运算符 >< 参数2 >
例如:a + b等价于operator + (a,b)。
对于成员函数来说,一个操作数通过this指针隐式的传递,(即本身),另一个操作数作为函数的参数显示的传递;对于友元函数(非成员函数)两个操作数都是通过参数来传递的。
(1)一般来说,单目运算符重载为类的成员函数,双目运算符重载为类的友元函数
(2)双目运算符不能将 = ()【】 -> 重载为类的友元函数。
(3)如果运算符的第一次操作数要求为隐式转换则必须为友元函数。
(4)当最左边的要求为类对象,而右边的是一个内置类型,则要为友元函数。
(5)当需要重载运算符具有可交换性时,选择重载为友元函数。若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
注意点:
T1 = T2 + T3;
可以为T1 = T2.operator+(T3);
也可以为T1 = operator+(T2,T3);
但是这两种方式不能同时声明定义,因为这会出现二义性。
如同“++”运算符有前缀和后缀两种使用形式一样,“++”和“–”重载运算符也有前缀和后缀两种运算符重载形式,以“++”重载运算符为例,其语法格式如下:
<函数类型> operator ++();
//成员函数形式重载前缀运算,++*this
<函数类型> operator ++(int); //仅标识作用,表明后加加。不需参数
//成员函数形式重载后缀运算,*this++
使用前缀运算符的语法格式如下:
++<对象>;
使用后缀运算符的语法格式如下:
<对象>++;
例:++成员函数重载示例。
class Increase
{
public:
Increase(int x) : value(x) {} //构造函数
Increase &operator++(); //前增量 , ++*this
Increase operator++(int); //后增量, *this++
void display(); //输出
private:
int value;
};
//前加加,++*this, C语言中int a = 10,
//支持++(++a), 即返回的是空间,对象引用
Increase &Increase::operator++()
{
value++; //先增量
return *this; //再返回原对象
}
//后加加,*this++, C语言中int a = 10,
//不支持(a++)++,即返回的是a的值,a再加1
Increase Increase::operator++(int)
{
Increase temp(*this); //返回原来的值,
//先做原对象备份
value++; //返回前值加1
return temp; //返回备份值,即加1前的值
}
//输出函数
void Increase::display()
{
cout << "the value is " << value << endl;
}
//主函数
int main()
{
//测试C语言中前++,后++用法,理解重载自增运算符的返回值
int a = 10;
++(++a);
cout << a << endl; //输出12
//(a++)++; //编译错误
cout << a << endl;
Increase count1(10); (++(++count1)).display(); //连续加两次,输出12
(count1++).display(); //先输出,再加。输出12,值变为13
count1.display(); //输出13 return 0;
}
C++语言允许的类型转换有4种:
标准类型->标准类型
标准类型->类类型
类类型->标准类型
类类型->类类型
标准类型是除class,struct和union类型外的其它所有类型。
标准类型转换为类类型
例:
class INTEGER
{
int num;
public:
INTEGER(int n) : num(n) //构造函数,参数整型
{
cout << "con " << num << " " << endl;
}
INTEGER(const char *str) : num(strlen(str)) //构造函数,参数字符串
{
cout << "con by str " << str << " " << num << endl;
}
void mem_fun(INTEGER anint)
{
num = anint.num;
cout << "mem_fun " << num << endl;
}
};
int main()
{
INTEGER obj1 = INTEGER(1);
//调用带整型参数的构造函数
INTEGER obj2 = "ChengDu";
//调用带字符串参数的构造函数
int anint = 10;
INTEGER obj3 = INTEGER(anint);
//调用带整型参数的构造函数
obj1 = 20; //obj1=(INTEGER(20));
//整型到类类型转换,调用带整型参数的构造函数
obj2.mem_fun(3); //obj2.mem_fun(INTEGER(3));
//整型到类类型转换,调用带整型参数的构造函数
return 0;
}
INTEGER obj1= INTEGER(1);将1转换为类类型。
INTEGER obj2=“ChengDu”; 编译尝试用构造函数。INTEGER(const char*)对赋值号右边的字符串进行类类型转换,转换成功后,赋给INTEGER的对象obj2。
语句obj2.mem_fun(3);中函数mem_fun由于需要一个INTEGER的对象作为参数,故尝试用构造函数对实参进行转换,转换成功后,进行虚实参数匹配,执行函数调用。这样的转换是系统自动做的,称为隐式类型转换。
类类型转换成标准类型及类类型
类需提供以下成员函数:
转换运算符:operator 类型名(); 类型名指明了返回类型。operator前不需返回类型
注意:没有返回类型。只能是非静态成员函数。
例:INTEGER中加成员函数:
operator int()
{
return num;
}
INTEGER A(10); int n = (int)A; //n=10
cout << int(A) << endl; //输出10
C++的I/O流库的一个重要特性就是能够支持新的数据类型的输出和输入。用户可以通过对插入符(<<)和提取符(>>)进行重载来支持新的数据类型。
使用友元函数
例:
#include
using namespace std;
class Date
{
public:
Date(int y, int m, int d) : Year(y), Month(m), Day(d) {}
friend ostream &operator<<(ostream &stream, Date &date); //友元
friend istream &operator>>(istream &stream, Date &date); //友元
private:
int Year, Month, Day;
};
//重载输出
ostream &operator<<(ostream &stream, Date &date)
{
stream << date.Year << "/" << date.Month << "/" << date.Day << endl;
return stream;
}
//重载输入
istream &operator>>(istream &stream, Date &date)
{
stream >> date.Year >> date.Month >> date.Day;
return stream;
}
int main()
{
Date Cdate(2004, 1, 1);
cout << "Current date:" << Cdate << endl;
cout << "Enter new date:";
cin >> Cdate;
cout << "New date:" << Cdate << endl;
return 0;
}
/*输出结果为:
Current date:2004/1/1
Enter new date:2021 5 25
New date:2021/5215
*/
//重载赋值运算符, 必须成员函数形式重载。
//*this = rhs 。
cvector &cvector::operator=(const cvector &rhs)
{
if(this == &rhs) //若相等,不做操作
return *this;
delete []data; //不相等,先释放原空间
n = rhs.n; //赋值
data = new int[n]; //分空间
memcpy(data, rhs.data, sizeof(int)*n); //元素赋值
return *this;
}
下标运算符只能作类成员运算符进行重载,不可作为友元运算符。
重载下标运算符的最大好处是提供一种向量访问的安全方法。
例:
#include
using namespace std;
class Demo {
int Vector[5];
public:
Demo() {};
int &operator[ ](int i) {return Vector[i]; }
};
int main()
{
Demo v;
for(int i=0;i<5;i++)
v[i]=i+1;
for(int i=0;i<5;i++)
cout<<v[i]<<" ";
cout<<endl;
}
/*程序运行结果:
1 2 3 4 5*/
重载后和普通的函数调用极其相似,所以又被称为仿函数。
类型 operator()(参数表)
例:
#include
#include
using namespace std;
//函数调用运算符重载也叫作仿函数
class Print
{
public:
void operator()(string text)
{
cout << text << endl;
}
};
class Add
{
public:
int operator()(int a, int b)
{
int c = a + b;
return c;
}
};
void test1()
{
Print p;
p("hello world");
}
void test2()
{
Add a;
int ret = a(10, 20);
cout << ret << endl;
//匿名对象使用函数调用运算符重载
//匿名对象不需要建立具体对象,在函数使用完成后,系统会自动释放匿名对象
cout << Add()(10, 20) << endl;
}
int main()
{
test1();
test2();
return 0;
}
输出:
hello world
30
30
掌握友元函数形式、成员函数形式重载。
掌握以下运算符的重载:
算术运算符,+、-、*、%、+=、-=等。
关系运算符,>、<、>=、!=、==等。
类型转换,operator 类型()。
自增、自减, 前++、后++等。
函数调用运算符,类型 operator()(参数表)。
下标,类型 &operator[](下标)。
重载输入、输出。
重载赋值,类类型 &operator=(类对象)