运算符重载:
1. 成员函数,运算符重载
单目运算符作为类成员函数重载时没有型参(除了后置自增(自减)有一个整型参数:
双目运算符作为类成员函数重载时只有一个型参,作为运算符的右操作数,其左操作数就是本对象自己,也就是this。
2.友元函数,运算符重载(全局范围)
友元函数(友元函数则是指某些虽然不是类成员却能够访问类的所有成员的函数)进行重载,那么它就不存在this指针了,所以需要定义两个参数来运算(对于双目运算符),
而友元函数的实现可以在外面定义,但必须在类内部声明。
声明为友元函数的好处:
和普通函数重载相比,它能够访问非公有成员。
将双目运算符重载为友元函数,这样就可以使用交换律。
成员函数内部,第一个参数必须是当前对象,但是全局函数中,两个参数度可以是对象(因为可以进行类型的隐式转换)
弊端:
运算符重载的格式为:
返回值类型 operator 运算符名称 (形参表列){
//TODO:
}
1.普通成员函数重载
#include
using namespace std;
class complex{
public:
complex();
complex(double real, double imag);
public:
//声明运算符重载
complex operator+(const complex &A) const;
void display() const;
private:
double m_real; //实部
double m_imag; //虚部
};
complex::complex(): m_real(0.0), m_imag(0.0){ }
complex::complex(double real, double imag): m_real(real), m_imag(imag){ }
//实现运算符重载
complex complex::operator+(const complex &A) const{
complex B;
B.m_real = this->m_real + A.m_real;
B.m_imag = this->m_imag + A.m_imag;
return B;
}
//同理,使用匿名对象
complex complex::operator+(const complex &A)const{
return complex(this->m_real + A.m_real, this->m_imag + A.m_imag);
}
本质:当执行c3 = c1 + c2;
语句时,编译器检测到+
号左边(+
号具有左结合性,所以先检测左边)是一个 complex 对象,就会调用成员函数operator+()
c3 = c1.operator+(c2);
2.非成员函数重载:使用友元函数(全局范围)
//在全局范围内重载+
complex operator+(const complex &A, const complex &B){
complex C;
C.m_real = A.m_real + B.m_real;
C.m_imag = A.m_imag + B.m_imag;
return C;
}
当执行c3 = c1 + c2;
语句时,编译器检测到+
号两边都是 complex 对象,就会转换为类似下面的函数调用:
c3 = operator+(c1, c2);
->
、下标运算符[ ]
、函数调用运算符( )
、赋值运算符=
只能以成员函数的形式重载。1.能够重载
+ - * / % ^ & | ~ ! = < > += -= = /= %= ^= &= |= << >> <<= >>= == != <= >= && || ++ – , -> -> () [] new new[] delete delete[]
2.不能重载
长度运算符sizeof
条件运算符: ?
成员选择符.
域解析运算符::
不能被重载。
c4 = c1 + c2 * c3;
//等价于
c4 = c1 + ( c2 * c3 );
//乘法的优先级仍然高于加法,并且它们仍然是二元运算符。
1.运算符重载函数作为类的成员函数时
2.将运算符重载函数作为全局函数时
注意事项:
一般都需要在类中将该函数声明为友元函数。
->
、下标运算符[ ]
、函数调用运算符( )
、赋值运算符=
只能以成员函数的形式重载。复数改写
#pragma once
#ifndef _Complex_
#define _Complex_
#include
#include
using namespace std;
class Complex
{
public:
Complex(double real=0.0, double imag=0.0) :m_real(real), m_imag(imag) {}
~Complex(){}
//全局函数重载运算符
friend Complex operator+(const Complex &com1, const Complex &com2);
friend Complex operator-(const Complex &com1, const Complex &com2);
friend Complex operator*(const Complex &com1, const Complex &com2);
friend Complex operator/(const Complex &com1, const Complex &com2);
friend bool operator==(const Complex &com1, const Complex &com2);
friend bool operator!=(const Complex &com1, const Complex &com2);
//成员函数形式重载
Complex& operator+=(const Complex &com1);
Complex& operator-=(const Complex &com1);
Complex& operator*=(const Complex &com1);
Complex& operator/=(const Complex &com1);
//成员函数
double getReal()const {return this->m_real;}
double getImag()const { return this->m_imag; }
private:
double m_real;
double m_imag;
};
#endif // !_Complex_
#include "Complex.h"
//重载+运算符
Complex operator+(const Complex &com1, const Complex &com2)
{
return Complex(com1.m_real + com2.m_real, com1.m_imag + com1.m_imag);
}
//重载-运算符
Complex operator-(const Complex &com1, const Complex &com2)
{
return Complex(com1.m_real - com2.m_real, com1.m_imag - com1.m_imag);
}
//重载*运算符 (a+bi) * (c+di) = (ac-bd) + (bc+ad)i
Complex operator*(const Complex &com1, const Complex &com2)
{
Complex c;
c.m_real = com1.m_real*com2.m_real - com1.m_imag*com1.m_imag;
c.m_imag = com1.m_imag*com2.m_real + com1.m_real*com2.m_imag;
return c;
}
//重载/运算符 (a+bi) / (c+di) = [(ac+bd) / (c²+d²)] + [(bc-ad) / (c²+d²)]i
Complex operator/(const Complex &com1, const Complex &com2)
{
Complex c;
c.m_real = (com1.m_real*com2.m_real + com1.m_imag*com1.m_imag)/(pow(com2.m_imag,2)+pow(com2.m_real,2));
c.m_imag = (com1.m_imag*com2.m_real - com1.m_real*com2.m_imag) / (pow(com2.m_imag, 2) + pow(com2.m_real, 2));
return c;
}
//重载== 符号
bool operator==(const Complex &com1, const Complex &com2)
{
return (com1.m_real == com2.m_real) && (com1.m_imag == com2.m_imag);
}
//重载!=符号
bool operator!=(const Complex &com1, const Complex &com2)
{
return !((com1.m_real == com2.m_real) && (com1.m_imag == com2.m_imag));
}
//重载+=符号
Complex& Complex::operator+=(const Complex &com1)
{
this->m_real += com1.m_real;
this->m_imag += com1.m_imag;
return *this;
}
//重载-=符号
Complex& Complex::operator-=(const Complex &com1)
{
this->m_real -= com1.m_real;
this->m_imag -= com1.m_imag;
return *this;
}
//重载*=符号
Complex& Complex::operator*=(const Complex &com1)
{
this->m_real = this->m_real*com1.m_real - this->m_imag*com1.m_imag;
this->m_imag = this->m_imag*com1.m_real - this->m_real*com1.m_imag;
return *this;
}
//重载/=符号
Complex& Complex::operator/=(const Complex &com1)
{
this->m_real = (this->m_real*com1.m_real + this->m_imag*com1.m_imag) / (pow(com1.m_real, 2) + pow(com1.m_imag, 2));
this->m_imag = (this->m_imag*com1.m_real - this->m_real*com1.m_imag) / (pow(com1.m_real, 2) + pow(com1.m_imag, 2));
return *this;
}
全局函数的好处
//全局
friend Complex operator+(const Complex &c1, const Complex &c2);
//成员,记得加=号,“+=”
Complex & operator+=(const Complex &c);
全局函数:
b = 2.2 + a
;,也是一样的道理,被转换为b = operator+(2.2, a)
;这样的形式,然后又调用转换构造函数将 2.2 转换为一个匿名 complex 对象complex(2.2)成员函数:
b = a + 1.1
;,被转换为b = a.operator+(1.1)
;,1.1 也是被转换为complex(1.1);b = 2.2 + a
;,被转换为b = (2.2).operator+(a)
;,这很显然是不正确
的,进而编译报错;总结:
被对称的处理
,小数(double)在 + 左边和右边都是正确的函数的选择标准:
核心:
重载输入输出运算符必须使用友元函数的形式
因为第一个参数时istream和ostream对象,如果使用成员函数,必须修改标准库的文件。
//重载输入运算符
istream& operator>>(iostream &in, Complex &com1)
{
in >> com1.m_real >> com1.m_imag;
return in;
}
//重载输出操作符
ostream& operator<<(ostream &out, Complex &com1)
{
out << com1.m_real << "+" << com1.m_imag << "+";
return out;
}
cout <<"c1 and c2"<< c1 << c2 << endl;
返回引用原因
C++规定,下标运算符[ ]
必须以成员函数的形式进行重载
申明一:可访问,可修改
返回值类型 & operator[ ] (参数);
申明二:只访问,不修改。(为了适应const对象)
const 返回值类型 & operator[ ] (参数) const;
提供const函数原因:
#include
using namespace std;
class Array{
public:
Array(int length = 0);
~Array();
public:
int & operator[](int i);
const int & operator[](int i) const;
public:
int length() const { return m_length; }
void display() const;
private:
int m_length; //数组长度
int *m_p; //指向数组内存的指针
};
Array::Array(int length): m_length(length){
if(length == 0){
m_p = NULL;
}else{
m_p = new int[length];
}
}
Array::~Array(){
delete[] m_p;
}
int& Array::operator[](int i){
return m_p[i];
}
const int & Array::operator[](int i) const{
return m_p[i];
}
void Array::display() const{
for(int i = 0; i < m_length; i++){
if(i == m_length - 1){
cout<<m_p[i]<<endl;
}else{
cout<<m_p[i]<<", ";
}
}
}
int main(){
int n;
cin>>n;
Array A(n);
for(int i = 0, len = A.length(); i < len; i++){
A[i] = i * 5;
}
A.display();
const Array B(n);
cout<<B[n-1]<<endl; //访问最后一个元素
return 0;
}
类型强制转换运算符是单目运算符,只能重载为成员函数,不能重载为全局函数。
(类型名)对象
这个对对象进行强制类型转换的表达式就等价于对象.operator 类型名()
,#include
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
}
强制转换的运算符不需要返回值的类型,因为返回类型的值是已知的。
重载强制类型转换符后,编译器会自动在需要类型转换的地方判断是否能够隐式转换。
如例子中,double n = 2 + c;编译器在+号后面会自动判断是否有double类型的转换函数。
一般用于成员函数的重载
格式:
#include
#include
using namespace std;
//秒表类
class stopwatch{
public:
stopwatch(): m_min(0), m_sec(0){ }
public:
void setzero(){ m_min = 0; m_sec = 0; }
stopwatch run(); // 运行
stopwatch operator++(); //++i,前置形式
stopwatch operator++(int); //i++,后置形式
friend ostream & operator<<( ostream &, const stopwatch &);
private:
int m_min; //分钟
int m_sec; //秒钟
};
stopwatch stopwatch::run(){
++m_sec;
if(m_sec == 60){
m_min++;
m_sec = 0;
}
return *this;
}
//前置递增
stopwatch stopwatch::operator++(){
return run();
}
//后置
stopwatch stopwatch::operator++(int n){
stopwatch s = *this;
run();
return s;
}
注意:
内存管理运算符
一般般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内存时才会重载。
以成员函数重载
void * className::operator new( size_t size ){
//TODO:
}
以全局函数重载
void * operator new( size_t size ){
//TODO:
}
size_t
size_t 在头文件 中被定义为typedef unsigned int size_t;
,也就是无符号整型。
在重载 new 或 new[] 时,无论是作为成员函数还是作为全局函数,它的第一个参数必须是 size_t 类型。
size_t 表示的是要分配空间的大小,对于 new[] 的重载函数而言,size_t 则表示所需要分配的所有空间的总和。
void *
:
以成员函数重载
void className::operator delete( void *ptr){
//TODO:
}
以全局函数重载
void operator delete( void *ptr){
//TODO:
}
ptr为需要删除的指针。