运算符重载(三)

  • 必须是成员函数,不能是友元函数。
  • 没有参数(操作数是什么?)。
  • 不能指定返回类型(其实已经指定了)。
  • 函数原型:operator 类型名();

下面用代码来说明,先新建工程,将上次实现的Integer类添加至项目中来:

Integer.h:

#ifndef _INTEGER_H
#define _INTEGER_H

class Integer
{
public:
    Integer(int n);
    ~Integer();
    void display() const;
    //Integer& operator ++();//++运算符重载
    friend Integer& operator ++(Integer& i);//友元方式重载前置++
    friend Integer operator ++(Integer& i, int n);//友元方式重载后置++

    //Integer operator ++(int i);//后置++运算符重载,其中的参数并没啥用,只是为了和前置++形成重载
private:
    int n_;
};

#endif    //_INTEGER_H

Integer.cpp:

#include "Integer.h"
#include <iostream>
using namespace std;

Integer::Integer(int n)    : n_(n) {

}

Integer::~Integer()
{
}

//Integer& Integer::operator ++() {
//    ++n_;
//    return *this;
//}

Integer& operator ++(Integer& i) {
    ++i.n_;
    return i;
}

//Integer Integer::operator ++(int i) {
//    Integer tmp(n_);//构造一个临时对象
//    n_++;//紧接着再来修改n_
//    return tmp;//这时返回的是对象,而不是引用了,因为如果是引用,临时对象会被回收,则指向了一个无效的对象了
//}

Integer operator ++(Integer& i, int n) {
    Integer tmp(i.n_);//构造一个临时对象
    i.n_++;//紧接着再来修改n_
    return tmp;//这时返回的是对象,而不是引用了,因为如果是引用,临时对象会被回收,则指向了一个无效的对象了
}

void Integer::display() const{
    cout<<n_<<endl;
}

测试代码:

#include "Integer.h"
#include <iostream>
using namespace std;

int main(void) {
    Integer n(100);
    n = 200;//可以将一个整型转换成类类型,因为实现了转换构造函数
    n.display();
    return 0;
}

上面的输出的写法及输出结果显而易见,就不多说了,接下来实现加法运算的函数:

很遗憾,这样的写法目前还不支持:

这时需要重写类型转换运算符了,下面来实现一下:

再次编译运行:

实际上用一个更加直观的例子更能看明白:

结果:

这是隐式转换,其实也可以使用显示转换:

 直接来代码,这里为了方便直接将类写在main函数文件中,举一个数据库相关的例子: 

#include <iostream>
using namespace std;

class DB {
public:
    DB() {
        cout<<"DB ..."<<endl;
    }

    ~DB() {
        cout<<"~DB ..."<<endl;
    }

    void open() {//数据库打开
        cout<<"~open ..."<<endl;
    }

    void close() {//数据库关闭
        cout<<"~close ..."<<endl;
    }

    void query() {//数据库查询
        cout<<"~query ..."<<endl;
    }
};

int main(void) {

    DB* db = new DB();
    db->open();

    db->query();
    
    db->close();
    delete db;

    return 0;
}

编译运行:

但是说到数据库的关闭,可能在实际的程序中并没有这么简单,因为不知道它何时释放,我们想当数据库对象销毁的时候自动关闭,那首先能想到的办法是:

#include <iostream>
using namespace std;

class DB {
public:
    DB() {
        cout<<"DB ..."<<endl;
        open();
    }

    ~DB() {
        cout<<"~DB ..."<<endl;
        close();
    }

    void open() {//数据库打开
        cout<<"~open ..."<<endl;
    }

    void close() {//数据库关闭
        cout<<"~close ..."<<endl;
    }

    void query() {//数据库查询
        cout<<"~query ..."<<endl;
    }
};

int main(void) {

    DB* db = new DB();
    //db->open();

    db->query();
    
    //db->close();
    delete db;

    return 0;
}

其运行结果也是一样的,但是这种方案也不是很可取,一是构造里面的数据库打开比较耗时,在构造函数中不宜做太多事,另外在实际中也能难找到合适的delete销毁对象的地方,也就不能调用close()方法了,我们希望在对象的生命周期结束的时候能够被自动释放,可以采取另外一种方案,具体如下:

#include <iostream>
using namespace std;

class DBHelper {//将原来的类改名了
public:
    DBHelper() {
        cout<<"DB ..."<<endl;
    }

    ~DBHelper() {
        cout<<"~DB ..."<<endl;
    }

    void open() {//数据库打开
        cout<<"~open ..."<<endl;
    }

    void close() {//数据库关闭
        cout<<"~close ..."<<endl;
    }

    void query() {//数据库查询
        cout<<"~query ..."<<endl;
    }
};

class DB {//新声明一个类
public:
    DB() {
        db_ = new DBHelper();
    }

    ~DB() {
        delete db_;
    }

    DBHelper* operator->() {//重写指针运算符
        return db_;
    }
private:
    DBHelper* db_;
};

int main(void) {

    DB db;
    db->open();//为啥对象可以直接用指针调用,也就是由于DB重写了指针运算符了

    db->query();
    
    db->close();

    return 0;
}

编译运行:

这样写有什么好处呢?可以很灵活的释放动态对象,利用的是确定性析构的办法,DB对象在生命周期开始时一定会调用构造函数,而当生命周期结束之后一定会调用析构函数,从而释放了所包装的DBHelper对象,使得我们对内存的控制更加方便,实际上这也是智能指针实现的一个技巧,就相当于DB是一个智能指针【smart pointer:关于智能指针之后会仔细学习,巧妙的使用智能指能能够避免内存泄漏】,它包装了DBHelper

先回顾一下new的三种用法:new operator、operator new、placement new

下面来用代码说明以上三种new的用法:

#include <iostream>
using namespace std;

class Test {
public:
    Test(int n): n_(n) {
        cout<<"Test(int n): n_(n)"<<endl;
    }
    Test(const Test& other) {
        cout<<"Test(const Test& other)"<<endl;
    }
    ~Test() {
        cout<<"~Test()"<<endl;
    }
private:
    int n_;
};

int main(void) {
    Test* p1 = new Test(10);//它是new operator=operator new + 构造函数的调用
    return 0;
}

实际上可以debug一下既可看出是否是如上面所说的那样:

按F11跟踪进去:

跳出该方法,继续按F11跟踪,会发现调用了构造函数了:

通过这个过程可以清楚的了解到new operator操作的内部执行过程。

所以对于"new operator、operator new"的这两种new的用法已经清楚了,那接下来说明一下它的最后一种用法:“placement new”:

编译运行:

从结果打印来看是一样的,为了进一步说明,可以debug一下:

其中operator new是可以被重载的,但是new operator是不能的,下面来看下相关的语法:

  • void* operator new(size_t size)
  • void operator delete(void* p)
  • void operator delete(void* p, size_t size)
  • void* operator new(size_t size, const char* file, long line)
  • void operator delete(void* p, const char* file, long line)
  • void* operator new[](size_t size)
  • void operator delete[](void* p)
  • void operator delete[](void* p, size_t size)

一旦new运算符被重载了,对应的delete运算符也随之要被重载,是相匹配的,下面来实现下:

#include <iostream>
using namespace std;

class Test {
public:
    Test(int n): n_(n) {
        cout<<"Test(int n): n_(n)"<<endl;
    }
    Test(const Test& other) {
        cout<<"Test(const Test& other)"<<endl;
    }
    ~Test() {
        cout<<"~Test()"<<endl;
    }

    void* operator new(size_t size) {//重载operator new
        cout<<"void* operator new(size_t size)"<<endl;
        void* p = malloc(size);
        return p;
    }

    void operator delete(void* p) {
        cout<<"void operator delete(void* p)"<<endl;
        free(p);
    }

    int n_;
};

int main(void) {
    Test* p1 = new Test(10);//它是new operator=operator new + 构造函数的调用
    delete p1;
    char chunk[10];
    Test* p2 = new(chunk) Test(200);//placement new:是基于已有的地址来创建对象的,不会分配内存
    cout<<p2->n_<<endl;

    //Test* p3 = (Test*)chunk;
    Test* p3 = reinterpret_cast<Test*>(chunk);
    cout<<p3->n_<<endl;

    return 0;
}

编译运行:

这是为啥呢?因为下面有个placement new还没有重载,所以为了说明问题先将下面的语句注释掉,之后再来让其编译通过:

再次编译运行:

另外operator delete有两种方式,如下:

那能否共存呢?

编译运行:

可以看出可以共存,默认是调用第一个参数的operator delete,那如果将一个参数的注释掉,程序还会编译通过么?

看结果:

也就是说这两个operator delete都是与operator new相匹配的。

另外operator new分为局部重载和全局重载,像刚才的是属于局部重载,只针对Test类有关,而如果像下面这种new operator呢?

它会不会调用Test类中的operator new运算符呢?答案当然不会,因为这是属于全局的,我们也可以重载全局的operator new,如下:

#include <iostream>
using namespace std;

class Test {
public:
    Test(int n): n_(n) {
        cout<<"Test(int n): n_(n)"<<endl;
    }
    Test(const Test& other) {
        cout<<"Test(const Test& other)"<<endl;
    }
    ~Test() {
        cout<<"~Test()"<<endl;
    }

    void* operator new(size_t size) {//重载operator new
        cout<<"void* operator new(size_t size)"<<endl;
        void* p = malloc(size);
        return p;
    }

    /*void operator delete(void* p) {
        cout<<"void operator delete(void* p)"<<endl;
        free(p);
    }*/

    void operator delete(void* p, size_t size) {
        cout<<"void operator delete(void* p, size_t size)"<<endl;
        free(p);
    }

    int n_;
};

void* operator new(size_t size) {
    cout<<"global void* operator new(size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete(void* p) {
    cout<<"global void operator delete(void* p)"<<endl;
    free(p);
}

int main(void) {
    Test* p1 = new Test(10);//它是new operator=operator new + 构造函数的调用
    delete p1;

    char* str = new char;
    delete str;

    char chunk[10];
    /*Test* p2 = new(chunk) Test(200);//placement new:是基于已有的地址来创建对象的,不会分配内存
    cout<<p2->n_<<endl;

    //Test* p3 = (Test*)chunk;
    Test* p3 = reinterpret_cast<Test*>(chunk);
    cout<<p3->n_<<endl;*/

    return 0;
}

编译运行:

那如果是new一个数组呢?

可以重载带数组的函数,如下:

#include <iostream>
using namespace std;

class Test {
public:
    Test(int n): n_(n) {
        cout<<"Test(int n): n_(n)"<<endl;
    }
    Test(const Test& other) {
        cout<<"Test(const Test& other)"<<endl;
    }
    ~Test() {
        cout<<"~Test()"<<endl;
    }

    void* operator new(size_t size) {//重载operator new
        cout<<"void* operator new(size_t size)"<<endl;
        void* p = malloc(size);
        return p;
    }

    /*void operator delete(void* p) {
        cout<<"void operator delete(void* p)"<<endl;
        free(p);
    }*/

    void operator delete(void* p, size_t size) {
        cout<<"void operator delete(void* p, size_t size)"<<endl;
        free(p);
    }

    int n_;
};

void* operator new(size_t size) {
    cout<<"global void* operator new(size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete(void* p) {
    cout<<"global void operator delete(void* p)"<<endl;
    free(p);
}

void* operator new[](size_t size) {
    cout<<"global void* operator new[](size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete[](void* p) {
    cout<<"global void operator delete[](void* p)"<<endl;
    free(p);
}

int main(void) {
    Test* p1 = new Test(10);//它是new operator=operator new + 构造函数的调用
    delete p1;

    char* str = new char[100];
    delete[] str;

    char chunk[10];
    /*Test* p2 = new(chunk) Test(200);//placement new:是基于已有的地址来创建对象的,不会分配内存
    cout<<p2->n_<<endl;

    //Test* p3 = (Test*)chunk;
    Test* p3 = reinterpret_cast<Test*>(chunk);
    cout<<p3->n_<<endl;*/

    return 0;
}

再次编译运行:

可见这次就调用了我们重载的全局数组的operator new函数了。

下面还有这种重载形式没有使用:

那它的作用是什么呢?下面来使用一下:

#include <iostream>
using namespace std;

class Test {
public:
    Test(int n): n_(n) {
        cout<<"Test(int n): n_(n)"<<endl;
    }
    Test(const Test& other) {
        cout<<"Test(const Test& other)"<<endl;
    }
    ~Test() {
        cout<<"~Test()"<<endl;
    }

    void* operator new(size_t size) {//重载operator new
        cout<<"void* operator new(size_t size)"<<endl;
        void* p = malloc(size);
        return p;
    }

    /*void operator delete(void* p) {
        cout<<"void operator delete(void* p)"<<endl;
        free(p);
    }*/

    void operator delete(void* p, size_t size) {
        cout<<"void operator delete(void* p, size_t size)"<<endl;
        free(p);
    }

    void* operator new(size_t size, const char* file, long line) {
        cout<<file<<":"<<line<<endl;
        void* p = malloc(size);
        return p;
    }

    void operator delete(void* p, const char* file, long line) {
        cout<<file<<":"<<line<<endl;
        free(p);
    }

    int n_;
};

void* operator new(size_t size) {
    cout<<"global void* operator new(size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete(void* p) {
    cout<<"global void operator delete(void* p)"<<endl;
    free(p);
}

void* operator new[](size_t size) {
    cout<<"global void* operator new[](size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete[](void* p) {
    cout<<"global void operator delete[](void* p)"<<endl;
    free(p);
}

int main(void) {
    Test* p1 = new Test(10);//它是new operator=operator new + 构造函数的调用
    delete p1;

    char* str = new char[100];
    delete[] str;

    char chunk[10];
    /*Test* p2 = new(chunk) Test(200);//placement new:是基于已有的地址来创建对象的,不会分配内存
    cout<<p2->n_<<endl;

    //Test* p3 = (Test*)chunk;
    Test* p3 = reinterpret_cast<Test*>(chunk);
    cout<<p3->n_<<endl;*/

    return 0;
}

另外解决一下placement new还没重载造成下面的测试代码编译不过的问题:

#include <iostream>
using namespace std;

class Test {
public:
    Test(int n): n_(n) {
        cout<<"Test(int n): n_(n)"<<endl;
    }
    Test(const Test& other) {
        cout<<"Test(const Test& other)"<<endl;
    }
    ~Test() {
        cout<<"~Test()"<<endl;
    }

    void* operator new(size_t size) {//重载operator new
        cout<<"void* operator new(size_t size)"<<endl;
        void* p = malloc(size);
        return p;
    }

    /*void operator delete(void* p) {
        cout<<"void operator delete(void* p)"<<endl;
        free(p);
    }*/

    void operator delete(void* p, size_t size) {
        cout<<"void operator delete(void* p, size_t size)"<<endl;
        free(p);
    }

    void* operator new(size_t size, const char* file, long line) {
        cout<<file<<":"<<line<<endl;
        void* p = malloc(size);
        return p;
    }

    void operator delete(void* p, const char* file, long line) {
        cout<<file<<":"<<line<<endl;
        free(p);
    }

    void* operator new(size_t size, void* p) {
        return p;
    }

    int n_;
};

void* operator new(size_t size) {
    cout<<"global void* operator new(size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete(void* p) {
    cout<<"global void operator delete(void* p)"<<endl;
    free(p);
}

void* operator new[](size_t size) {
    cout<<"global void* operator new[](size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete[](void* p) {
    cout<<"global void operator delete[](void* p)"<<endl;
    free(p);
}

int main(void) {
    Test* p1 = new Test(10);//它是new operator=operator new + 构造函数的调用
    delete p1;

    char* str = new char[100];
    delete[] str;

    char chunk[10];
    Test* p2 = new(chunk) Test(200);//placement new:是基于已有的地址来创建对象的,不会分配内存
    cout<<p2->n_<<endl;

    //Test* p3 = (Test*)chunk;
    Test* p3 = reinterpret_cast<Test*>(chunk);
    cout<<p3->n_<<endl;

    return 0;
}

编译:

new和delete操作是需要匹配的,所以再重载一下delete方法:

#include <iostream>
using namespace std;

class Test {
public:
    Test(int n): n_(n) {
        cout<<"Test(int n): n_(n)"<<endl;
    }
    Test(const Test& other) {
        cout<<"Test(const Test& other)"<<endl;
    }
    ~Test() {
        cout<<"~Test()"<<endl;
    }

    void* operator new(size_t size) {//重载operator new
        cout<<"void* operator new(size_t size)"<<endl;
        void* p = malloc(size);
        return p;
    }

    /*void operator delete(void* p) {
        cout<<"void operator delete(void* p)"<<endl;
        free(p);
    }*/

    void operator delete(void* p, size_t size) {
        cout<<"void operator delete(void* p, size_t size)"<<endl;
        free(p);
    }

    void* operator new(size_t size, const char* file, long line) {
        cout<<file<<":"<<line<<endl;
        void* p = malloc(size);
        return p;
    }

    void operator delete(void* p, const char* file, long line) {
        cout<<file<<":"<<line<<endl;
        free(p);
    }

    void* operator new(size_t size, void* p) {
        return p;
    }

    void operator delete(void*, void* p) {//关于它的函数原形可能通过debug内部跟踪得到
    }

    int n_;
};

void* operator new(size_t size) {
    cout<<"global void* operator new(size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete(void* p) {
    cout<<"global void operator delete(void* p)"<<endl;
    free(p);
}

void* operator new[](size_t size) {
    cout<<"global void* operator new[](size_t size)"<<endl;
    void* p = malloc(size);
    return p;
}

void operator delete[](void* p) {
    cout<<"global void operator delete[](void* p)"<<endl;
    free(p);
}

int main(void) {
    Test* p1 = new Test(10);//它是new operator=operator new + 构造函数的调用
    delete p1;

    char* str = new char[100];
    delete[] str;

    char chunk[10];
    Test* p2 = new(chunk) Test(200);//placement new:是基于已有的地址来创建对象的,不会分配内存
    cout<<p2->n_<<endl;

    //Test* p3 = (Test*)chunk;
    Test* p3 = reinterpret_cast<Test*>(chunk);
    cout<<p3->n_<<endl;

    return 0;
}

再次编译运行:

修改代码如下:

再次编译运行,看这回构造与虚构是否匹配了:

好了,解决了placement new编译通过的问题之后,则下面来使用一下带多个参数的operator new的重载方法,如下:

下面来看下结果:

照理应该是调用与之匹配的它:

具体原因这里也不太清楚,反正如果不重写这个delete会给出警告,这个待之后再做出考证。对于上面这种写法其实可以定义成宏,如下:

转载于:https://www.cnblogs.com/webor2006/p/5431368.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值