第6章 C++运算符重载总结

C++运算符重载的概念和语法

下面的代码定义了一个复数类,通过运算符重载,可以用+号实现复数的加法运算:

#include <iostream>
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;
}
void complex::display() const {
    cout << m_real << " + " << m_imag << "i" << endl;
}
int main() {
    complex c1(4.3, 5.8);
    complex c2(2.4, 3.7);
    complex c3;
    c3 = c1 + c2;
    c3.display();
    return 0;
}

运算符重载其实就是定义一个函数,在函数体内实现想要的功能,当用到该运算符时,编译器会自动调用这个函数。也就是说,运算符重载是通过函数实现的,它本质上是函数重载

运算符重载的格式为:

返回值类型 operator 运算符名称 (形参表列){
    //TODO:
}
  • operator是关键字,专门用于定义重载运算符的函数
  • 我们可以将"operator 运算符名称"这一部分看做函数名。对于上面的代码,函数名就是operator+。
  • 运算符重载函数除了函数名有特定的格式,其它地方和普通函数并没有区别

上面的例子中,我们在 complex 类中重载了运算符+,该重载只对 complex 对象有效。当执行c3 = c1 + c2;语句时,编译器检测到+号左边(+号具有左结合性,所以先检测左边是一个 complex 对象,就会调用成员函数operator+()也就是转换为下面的形式:  

c3 = c1.operator+(c2);

运算符重载函数不仅可以作为类的成员函数,还可以作为全局函数。(个人:由于重载的全局运算符+的函数体里要访问complex的私有成员变量,所以我们需要在complex的类定义里面将operator+声明为这个类的友元函数,这样才能访问complex的私有成员变量)

#include <iostream>
using namespace std;
class complex{
public:
    complex();
    complex(double real, double imag);
public:
    void display() const;
    //声明为友元函数
    friend complex operator+(const complex &A, const complex &B);
private:
    double m_real;
    double m_imag;
};
complex operator+(const complex &A, const complex &B);
complex::complex(): m_real(0.0), m_imag(0.0){ }
complex::complex(double real, double imag): m_real(real), m_imag(imag){ }
void complex::display() const{
    cout<<m_real<<" + "<<m_imag<<"i"<<endl;
}
//在全局范围内重载+
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;
}
int main(){
    complex c1(4.3, 5.8);
    complex c2(2.4, 3.7);
    complex c3;
    c3 = c1 + c2;
    c3.display();
    return 0;
}

当执行c3 = c1 + c2;语句时,编译器检测到+号两边都是 complex 对象,就会转换为类似下面的函数调用:  

c3 = operator+(c1, c2);

虽然运算符重载所实现的功能完全可以用函数替代,但运算符重载使得程序的书写更加人性化,易于阅读。运算符被重载后,原有的功能仍然保留,没有丧失或改变。通过运算符重载,扩大了C++已有运算符的功能,使之能用于对象。  

C++运算符重载的规则

运算符重载的注意事项

  • 并不是所有的运算符都可以重载。能够重载的运算符包括:

 上述运算符中,[]是下标运算符()是函数调用运算符。自增自减运算符的前置和后置形式都可以重载。

长度运算符sizeof、条件运算符: ?、成员选择符.和域解析运算符::不能被重载。(个人:能够重载的运算符太多了,只要记住不能重载的就行了)  

  • 重载不能改变运算符的优先级和结合性
  • 重载不会改变运算符的用法,原有几个操作数、操作数在左边还是在右边,这些都不会改变。例如~号右边只有一个操作数,+号总是出现在两个操作数之间,重载后也必须如此。
  • 运算符重载函数不能有默认的参数,否则就改变了运算符操作数的个数,这显然是错误的
  • 运算符重载函数既可以作为类的成员函数,也可以作为全局函数
    • 将运算符重载函数作为类的成员函数时,二元运算符的参数只有一个,一元运算符不需要参数。之所以少一个参数,是因为这个参数是隐含的
    • 将运算符重载函数作为全局函数时,二元操作符就需要两个参数,一元操作符需要一个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符,防止程序员修改用于内置类型的运算符的性质,例如:
      complex operator+(int a, complex &c){
          return complex(a+c.real, c.imag);
      }
      
    • 另外,将运算符重载函数作为全局函数时,一般都需要在类中将该函数声明为友元函数。原因很简单,该函数大部分情况下都需要使用类的private 成员。  

  • 箭头运算符->、下标运算符[ ]、函数调用运算符( )、赋值运算符=只能以成员函数的形式重载

C++重载数学运算符

四则运算符(+、-、*、/、+=、-=、*=、/=)关系运算符(>、<、<=、>=、==、!=)都是数学运算符,它们在实际开发中非常常见,被重载的几率也很高,并且有着相似的重载格式

本节以复数类 Complex 为例对它们进行重载,重在演示运算符重载的语法以及规范。  复数能够进行完整的四则运算,但不能进行完整的关系运算:我们只能判断两个复数是否相等,但不能比较它们的大小,所以不能对 >、<、<=、>= 进行重载。下面是具体的代码

#include <iostream>
#include <cmath>
using namespace std;
//复数类
class Complex{
public:  //构造函数
    Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }
public:  //运算符重载
    //以全局函数的形式重载
    friend Complex operator+(const Complex &c1, const Complex &c2);
    friend Complex operator-(const Complex &c1, const Complex &c2);
    friend Complex operator*(const Complex &c1, const Complex &c2);
    friend Complex operator/(const Complex &c1, const Complex &c2);
    friend bool operator==(const Complex &c1, const Complex &c2);
    friend bool operator!=(const Complex &c1, const Complex &c2);
    //以成员函数的形式重载
    Complex & operator+=(const Complex &c);
    Complex & operator-=(const Complex &c);
    Complex & operator*=(const Complex &c);
    Complex & operator/=(const Complex &c);
public:  //成员函数
    double real() const{ return m_real; }
    double imag() const{ return m_imag; }
private:
    double m_real;  //实部
    double m_imag;  //虚部
};
//重载+运算符
Complex operator+(const Complex &c1, const Complex &c2){
    Complex c;
    c.m_real = c1.m_real + c2.m_real;
    c.m_imag = c1.m_imag + c2.m_imag;
    return c;
}
//重载-运算符
Complex operator-(const Complex &c1, const Complex &c2){
    Complex c;
    c.m_real = c1.m_real - c2.m_real;
    c.m_imag = c1.m_imag - c2.m_imag;
    return c;
}
//重载*运算符  (a+bi) * (c+di) = (ac-bd) + (bc+ad)i
Complex operator*(const Complex &c1, const Complex &c2){
    Complex c;
    c.m_real = c1.m_real * c2.m_real - c1.m_imag * c2.m_imag;
    c.m_imag = c1.m_imag * c2.m_real + c1.m_real * c2.m_imag;
    return c;
}
//重载/运算符  (a+bi) / (c+di) = [(ac+bd) / (c²+d²)] + [(bc-ad) / (c²+d²)]i
Complex operator/(const Complex &c1, const Complex &c2){
    Complex c;
    c.m_real = (c1.m_real*c2.m_real + c1.m_imag*c2.m_imag) / (pow(c2.m_real, 2) + pow(c2.m_imag, 2));
    c.m_imag = (c1.m_imag*c2.m_real - c1.m_real*c2.m_imag) / (pow(c2.m_real, 2) + pow(c2.m_imag, 2));
    return c;
}
//重载==运算符
bool operator==(const Complex &c1, const Complex &c2){
    if( c1.m_real == c2.m_real && c1.m_imag == c2.m_imag ){
        return true;
    }else{
        return false;
    }
}
//重载!=运算符
bool operator!=(const Complex &c1, const Complex &c2){
    if( c1.m_real != c2.m_real || c1.m_imag != c2.m_imag ){
        return true;
    }else{
        return false;
    }
}
//重载+=运算符
Complex & Complex::operator+=(const Complex &c){
    this->m_real += c.m_real;
    this->m_imag += c.m_imag;
    return *this;
}
//重载-=运算符
Complex & Complex::operator-=(const Complex &c){
    this->m_real -= c.m_real;
    this->m_imag -= c.m_imag;
    return *this;
}
//重载*=运算符
Complex & Complex::operator*=(const Complex &c){
    this->m_real = this->m_real * c.m_real - this->m_imag * c.m_imag;
    this->m_imag = this->m_imag * c.m_real + this->m_real * c.m_imag;
    return *this;
}
//重载/=运算符
Complex & Complex::operator/=(const Complex &c){
    this->m_real = (this->m_real*c.m_real + this->m_imag*c.m_imag) / (pow(c.m_real, 2) + pow(c.m_imag, 2));
    this->m_imag = (this->m_imag*c.m_real - this->m_real*c.m_imag) / (pow(c.m_real, 2) + pow(c.m_imag, 2));
    return *this;
}
int main(){
    Complex c1(25, 35);
    Complex c2(10, 20);
    Complex c3(1, 2);
    Complex c4(4, 9);
    Complex c5(34, 6);
    Complex c6(80, 90);

    Complex c7 = c1 + c2;
    Complex c8 = c1 - c2;
    Complex c9 = c1 * c2;
    Complex c10 = c1 / c2;
    cout<<"c7 = "<<c7.real()<<" + "<<c7.imag()<<"i"<<endl;
    cout<<"c8 = "<<c8.real()<<" + "<<c8.imag()<<"i"<<endl;
    cout<<"c9 = "<<c9.real()<<" + "<<c9.imag()<<"i"<<endl;
    cout<<"c10 = "<<c10.real()<<" + "<<c10.imag()<<"i"<<endl;

    c3 += c1;
    c4 -= c2;
    c5 *= c2;
    c6 /= c2;
    cout<<"c3 = "<<c3.real()<<" + "<<c3.imag()<<"i"<<endl;
    cout<<"c4 = "<<c4.real()<<" + "<<c4.imag()<<"i"<<endl;
    cout<<"c5 = "<<c5.real()<<" + "<<c5.imag()<<"i"<<endl;
    cout<<"c6 = "<<c6.real()<<" + "<<c6.imag()<<"i"<<endl;

    if(c1 == c2){
        cout<<"c1 == c2"<<endl;
    }
    if(c1 != c2){
        cout<<"c1 != c2"<<endl;
    }

    return 0;
}

到底以成员函数还是全局函数(友元函数)的形式重载运算符  

上一节,我们以全局函数的形式重载了 +、-、*、/、==、!=,以成员函数的形式重载了 +=、-=、*=、/=,而没有一股脑都写成全局函数或者成员函数,这样做是有原因的,这节我们就来分析一下。

为了搞清成员函数和全局函数的区别,在分析以前,我们先来了解一个概念,叫做「转换构造函数」

#include <iostream>
using namespace std;
//复数类
class Complex {
public:
    Complex() : m_real(0.0), m_imag(0.0) { }
    Complex(double real, double imag) : m_real(real), m_imag(imag) { }
    Complex(double real) : m_real(real), m_imag(0.0) { }  //转换构造函数
public:
    friend Complex operator+(const Complex& c1, const Complex& c2);
public:
    double real() const { return m_real; }
    double imag() const { return m_imag; }
private:
    double m_real;  //实部
    double m_imag;  //虚部
};
//重载+运算符
Complex operator+(const Complex& c1, const Complex& c2) {
    Complex c;
    c.m_real = c1.m_real + c2.m_real;
    c.m_imag = c1.m_imag + c2.m_imag;
    return c;
}

int main() {
    Complex c1(25, 35);
    Complex c2 = c1 + 15.6;
    Complex c3 = 28.23 + c1;
    cout << c2.real() << " + " << c2.imag() << "i" << endl;
    cout << c3.real() << " + " << c3.imag() << "i" << endl;

    return 0;
}

请留意第 28、29 行代码,它说明Complex类型可以和 double 类型相加,这很奇怪,因为我们并没有对针对这两个类型重载 +,这究竟是怎么做到的呢?其实,编译器在检测到 Complex和double(小数默认为double类型)相加时,会先尝试将 double 转换为 Complex,或者反过来将 Complex 转换为 double(只有类型相同的数据才能进行 + 运算),如果都转换失败,或者都转换成功(产生了二义性),才报错。本例中,编译器会先通过构造函数Complex(double real);将 double转换为Complex,再调用重载过的+进行计算,整个过程类似于下面的形式:

也就是说,小数被转换成了匿名的Complex对象。在这个转换过程中,构造函数Complex(double real);起到了至关重要的作用,如果没有它,转换就会失败,Complex 也不能和 double 相加。Complex(double real);在作为普通构造函数的同时,还能将double 类型转换为Complex类型,集合了“构造函数”和“类型转换”的功能,所以被称为「转换构造函数。(个人:也就是一个构造函数的参数只有一个,且是别的类型,那么这个构造函数是转换构造函数),换句话说,转换构造函数用来将其它类型(可以是 bool、int、double 等基本类型,也可以是数组、指针、结构体、类等构造类型)转换为当前类类型

为什么要以全局函数的形式重载 +:(个人:也就是下面的两条理由)

  • 上面的例子中,我们定义的operator+是一个全局函数(一个友元函数),而不是成员函数,这样做是为了保证 + 运算符的操作数能够被对称的处理;换句话说,小数(double 类型)在 + 左边和右边都是正确的如果将operator+定义为成员函数,根据“+ 运算符具有左结合性”这条原则,(个人:因为+具有左结合性,会和左运算数结合,调用的会是左运算数的成员函数版本的重载+运算符),Complex c2 = c1 + 15.6;会被转换为下面的形式:
Complex c2 = c1.operator+(Complex(15.6));

这就是通过对象调用成员函数,是正确的。而对于Complex c3 = 28.23 + c1;,编译器会尝试转换为不同的形式:

Complex c3 = (28.23).operator+(c1);

很显然这是错误的,因为 double 类型并没有以成员函数的形式重载 +。也就是说,以成员函数的形式重载 +,只能计算c1 + 15.6,不能计算28.23 + c1,这是不对称的

  • C++ 只会对成员函数的参数进行类型转换,而不会对调用成员函数的对象进行类型转换。(个人:也就是C++不会对左操作数进行类型转换,只会对右操作数进行类型转换,这和+操作符左右两边的操作数地位应该是相等的惯例不符),以下面的语句为例: 
obj.func(params);

编译器不会尝试对 obj 进行任何类型转换,它有 func() 成员函数就调用,没有就报错。而对于实参 params,编译器会“拼命地”将它转换为形参的类型。  

为什么要以成员函数的形式重载 +=

我们首先要明白,

  • 运算符重载的初衷是给类添加新的功能,方便类的运算,它作为类的成员函数是理所应当的,是首选的不过,类的成员函数不能对称地处理数据,程序员必须在(参与运算的)所有类型的内部都重载当前的运算符。以上面的情况为例,我们必须在Complex和 double 内部都重载+运算符,这样做不但会增加运算符重载的数目,还要在许多地方修改代码,这显然不是我们所希望的,所以,C++进行了折中,允许以全局函数(友元函数)的形式重载运算符。 
  • C++ 创始人 Bjarne Stroustrup 也曾考虑过为内部类型(bool、int、double 等)定义额外运算符的问题,但后来还是放弃了这种想法,
    • 因为 Bjarne Stroustrup 不希望改变现有规则:任何类型(无论是内部类型还是用户自定义类型)都不能在其定义完成以后再增加额外的操作
    • 这里还有另外的一个原因,C内部类型之间的转换已经够肮脏了,决不能再向里面添乱。而通过成员函数为已存在的类型提供混合运算的方式,从本质上看,比我们所采用的全局函数(友元函数)加转换构造函数的方式还要肮脏许多
  • 采用全局函数能使我们定义这样的运算符,它们的参数具有逻辑的对称性
  • 与此相对应的,把运算符定义为成员函数能够保证在调用时对第一个(最左的)运算对象不出现类型转换,也就是上面提到的「C++ 不会对调用成员函数的对象进行类型转换

总起来说,

  • 有一部分运算符重载既可以是成员函数也可以是全局函数,虽然没有一个必然的、不可抗拒的理由选择成员函数,但我们应该优先考虑成员函数,这样更符合运算符重载的初衷;(个人:是自己定义的版本,放在自定义的类类型里比较恰当,运算符重载的初衷是给类添加新的功能,方便类的运算,它作为类的成员函数是理所应当的,是首选的)
  • 另外有一部分运算符重载必须是全局函数,这样能保证参数的对称性
  • 除了C++规定的几个特定的运算符外,暂时还没有发现必须以成员函数的形式重载的运算符。C++ 规定,箭头运算符->、下标运算符[ ]、函数调用运算符( )、赋值运算符=只能以成员函数的形式重载。(个人:对于赋值运算符,我们不希望改变左操作数的类型,相反,既然是赋值,我们要求赋值的右操作数的类型转换为左操作数的类型才能赋值) 

C++重载>>和<<(输入输出运算符)

  • 在C++中,标准库本身已经对左移运算符<<和右移运算符>>分别进行了重载,使其能够用于不同数据的输入输出,但是输入输出的对象只能是C++ 内置的数据类型(例如 bool、int、double 等)和标准库所包含的类类型(例如 string、complex、ofstream、ifstream 等)
  • 如果我们自己定义了一种新的数据类型,需要用输入输出运算符去处理,那么就必须对它们进行重载

本节以前面的 complex 类为例来演示输入输出运算符的重载。

本节要达到的目标是让复数的输入输出和 int、float 等基本类型一样简单。假设 num1、num2 是复数,

  • 那么输出形式就是:
cout<<num1<<num2<<endl;
  • 输入形式就是:
cin>>num1>>num2;

cout 是 ostream 类的对象,cin 是 istream 类的对象,要想达到这个目标,就必须以全局函数(友元函数)的形式重载<<>>,否则就要修改标准库中的类,这显然不是我们所期望的。(个人:因为cout和cin是左操作数,输入输出运算符具有左结合性,会调用左操作数的重载输入输出运算符,而cout和cin的类里重载的版本不包含我们需要的能够处理我们自定义的类型的版本,而标准库我们又不能自己去修改往里面添加,所以只能以全局函数的形式重载)  

重载输入运算符>>

istream & operator>>(istream &in, complex &A){
    in >> A.m_real >> A.m_imag;
    return in;
}

之所以返回 istream 类对象的引用,是为了能够连续读取复数,让代码书写更加漂亮,例如:

complex c1, c2;
cin>>c1>>c2;

如果不返回引用,那就只能一个一个地读取了:  

complex c1, c2;
cin>>c1;
cin>>c2;

另外,运算符重载函数中用到了complex类的private成员变量,必须在complex类中将该函数声明为友元函数:  

friend istream & operator>>(istream & in , complex &a);

重载输出运算符<<:  

ostream & operator<<(ostream &out, complex &A){
    out << A.m_real <<" + "<< A.m_imag <<" i ";
    return out;
}

由于采用了引用的方式进行参数传递,并且也返回了对象的引用,所以重载后的运算符可以实现连续输出。  

为了能够直接访问complex类的private成员变量,同样需要将该函数声明为complex类的友元函数:  

friend ostream & operator<<(ostream &out, complex &A);

完整代码:  

#include <iostream>
using namespace std;
class complex {
public:
    complex(double real = 0.0, double imag = 0.0) : m_real(real), m_imag(imag) { };
public:
    friend complex operator+(const complex& A, const complex& B);
    friend complex operator-(const complex& A, const complex& B);
    friend complex operator*(const complex& A, const complex& B);
    friend complex operator/(const complex& A, const complex& B);
    friend istream& operator>>(istream& in, complex& A);
    friend ostream& operator<<(ostream& out, complex& A);
private:
    double m_real;  //实部
    double m_imag;  //虚部
};
//重载加法运算符
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;
}
//重载减法运算符
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;
}
//重载乘法运算符
complex operator*(const complex& A, const complex& B) {
    complex C;
    C.m_real = A.m_real * B.m_real - A.m_imag * B.m_imag;
    C.m_imag = A.m_imag * B.m_real + A.m_real * B.m_imag;
    return C;
}
//重载除法运算符
complex operator/(const complex& A, const complex& B) {
    complex C;
    double square = A.m_real * A.m_real + A.m_imag * A.m_imag;
    C.m_real = (A.m_real * B.m_real + A.m_imag * B.m_imag) / square;
    C.m_imag = (A.m_imag * B.m_real - A.m_real * B.m_imag) / square;
    return C;
}
//重载输入运算符
istream& operator>>(istream& in, complex& A) {
    in >> A.m_real >> A.m_imag;
    cout<<"输入一个复数完毕!"<<endl;
    return in;
}
//重载输出运算符
ostream& operator<<(ostream& out, complex& A) {
    out << A.m_real << " + " << A.m_imag << " i ";;
    return out;
}
int main() {
    complex c1, c2, c3;
    cin >> c1 >> c2;
    c3 = c1 + c2;
    cout << "c1 + c2 = " << c3 << endl;
    c3 = c1 - c2;
    cout << "c1 - c2 = " << c3 << endl;
    c3 = c1 * c2;
    cout << "c1 * c2 = " << c3 << endl;
    c3 = c1 / c2;
    cout << "c1 / c2 = " << c3 << endl;
    return 0;
}

 C++重载[ ](下标运算符) 

C++ 规定,下标运算符[ ]必须以成员函数的形式进行重载。(个人:没办法,语言的规定),该重载函数在类中的声明格式如下:

返回值类型 & operator[ ] (参数);

或者:

const 返回值类型 & operator[ ] (参数) const;
  • 使用第一种声明方式,[ ]不仅可以访问元素,还可以修改元素
  • 使用第二种声明方式,[ ]只能访问而不能修改元素

在实际开发中,我们应该同时提供以上两种形式,这样做是为了适应 const 对象因为通过 const 对象只能调用 const 成员函数,如果不提供第二种形式,那么将无法访问 const 对象的任何元素

#include <iostream>
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;
}

 重载[ ]运算符以后,表达式arr[i]会被转换为

arr.operator[ ](i);  

C++重载++和--(自增自减运算符)  

自增++和自减--都是一元运算符,它的前置形式和后置形式都可以被重载。(个人:自增自减这个表达式返回的都是一个这个表达式产生的临时值,所以这两个运算符的返回类型都是值类型,而不是引用类型)

#include <iostream>
#include <iomanip>
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;
}
ostream& operator<<(ostream& out, const stopwatch& s) {
    out << setfill('0') << setw(2) << s.m_min << ":" << setw(2) << s.m_sec;
    return out;
}
int main() {
    stopwatch s1, s2;
    s1 = s2++;
    cout << "s1: " << s1 << endl;
    cout << "s2: " << s2 << endl;
    s1.setzero();
    s2.setzero();
    s1 = ++s2;
    cout << "s1: " << s1 << endl;
    cout << "s2: " << s2 << endl;
    return 0;
}

  • operator++() 函数实现自增的前置形式
  • operator++ (int n) 函数实现自增的后置形式在这个函数中参数n是没有任何意义的,它的存在只是为了区分是前置形式还是后置形式

C++重载new和delete运算符 

(个人:这里的讲解不清楚,跳过)

内存管理运算符new、new[]、delete 和 delete[]也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数。一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内存时才会重载。 

  • 以成员函数的形式重载 new 运算符:
void * className::operator new( size_t size ){
    //TODO:
}
  • 以全局函数的形式重载 new 运算符:
void * operator new( size_t size ){
    //TODO:
}

两种重载形式的返回值相同,都是void *类型,并且都有一个参数,为size_t类型。在重载 new 或 new[] 时,无论是作为成员函数还是作为全局函数,它的第一个参数必须是 size_t 类型。size_t 表示的是要分配空间的大小,对于 new[] 的重载函数而言,size_t 则表示所需要分配的所有空间的总和。当然,重载函数也可以有其他参数,但都必须有默认值,并且第一个参数的类型必须是 size_t

size_t 在头文件 <cstdio> 中被定义为typedef unsigned int size_t;,也就是无符号整型

同样的,delete 运算符也有两种重载形式。以类的成员函数的形式进行重载:

void className::operator delete( void *ptr){
    //TODO:
}

以全局函数的形式进行重载:

void operator delete( void *ptr){
    //TODO:
}

两种重载形式的返回值都是 void 类型,并且都必须有一个 void 类型的指针作为参数,该指针指向需要释放的内存空间。

当我们以类的成员函数的形式重载了new 和 delete 操作符,其使用方法如下:

C * c = new C;  //分配内存空间
//TODO:
delete c;  //释放内存空间

如果类中没有定义 new 和 delete 的重载函数,那么会自动调用内建的 new 和 delete 运算符。  

C++重载强制类型转换运算符   

  • 在 C++ 中,类型的名字(包括类的名字)本身也是一种运算符,即类型强制转换运算符。
  • 类型强制转换运算符是单目运算符,也可以被重载,个人:说是单目运算符是说这个运算符没有参数,强制转化为double和强制转化为int是两个不同的强制类型转换运算符)
  • 但只能重载为成员函数,不能重载为全局函数
  • 经过适当重载后,(类型名)对象这个对对象进行强制类型转换的表达式就等价于对象.operator 类型名()
  • 重载强制类型转换运算符时,不需要指定返回值类型,因为返回值类型是确定的,就是运算符本身代表的类型,在这里就是 double。

下面的程序对 double 类型强制转换运算符进行了重载。(个人:可以看到,在表达式中,如果运算的对象类型不符合条件,编译器会自动调用自定义的强制类型转换运算符或者转换构造函数)

#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
}

 

posted on 2022-09-14 23:56  朴素贝叶斯  阅读(448)  评论(0编辑  收藏  举报

导航