运算符重载

2019-04-29  本文已影响0人  漫游之光

运算符重载允许把标准运算符(如+、—、*、/、<、>等)应用于自定义数据类型的对象。直观自然,可以提高程序的可读性,体现了C++的可扩充性。运算符重载仅仅只是语法上的方便,它是另一种函数调用的方式。运算符重载,本质上是函数重载,所以不要滥用重载、因为它只是语法上的方便,所以只有在涉及的代码更容易写、尤其是更易读时才有必要重载。

运算符重载有两种方式,一种是重载为成员函数,函数原型如下。

返回值类型 operator 运算符(参数表);

还可以重载为友元函数的方式,函数原型如下。

friend 返回值类型 operator 运算符(参数表);

可重载的运算符有一元运算符和二元运算符,一元运算符有一个参数,二元运算符有两个参数,对应于函数调用的参数。重载为成员函数时,this指针默认为第一个参数,而重载为友元时需要显示表示所有参数。请看下面这个例子。

#include <iostream>
using namespace std;

class Integer{
    friend ostream& operator<<(ostream& out, const Integer& i);
public:
    Integer(int val):val_(val){}
    Integer& operator++(){
        val_++;
        return *this;
    }
    Integer operator++(int i){
        Integer temp(val_);
        val_++;
        return temp;
    }
    operator int(){
        return val_;
    }
private:
    int val_;
};

ostream& operator<<(ostream& out, const Integer& i){
    out<<i.val_;
    return out;
}

int main(int argc, char const *argv[])
{
    Integer i(2);
    cout<<i++<<endl; /* cout<<i.operator++()<<endl; */
    cout<<++i<<endl; /* cout<<i.operator++(0)<<endl; */
    int a = 1 + i;
    cout<<a<<endl;
    system("pause");
    return 0;
}

这个例子想说明的是,第一,可以手动调用重载的运算符;第二,因为++,--运算符分为前置和后置,所以在重载运算符的时候,后置的++,--需要增加一个int参数,这个参数没什么用,只是为了区分是前置还是后置。并且,可以看出,由于后置的++,--为了满足语义需要创建一个临时对象,所以用前置++,--的效率要高一点。

此外,运算符重载有以下的注意点。

operator 类型名();

运算符重载例子,String类的简单实现

String类需要重载的运算符为,=运算符,[]运算符重载,+运算符重载,+=运算符重载,<<运算符重载, > >运算符重载。下面是具体的代码。

#include <iostream>
#include <exception>
#include <string.h>
using namespace std;

class String{
    friend ostream& operator<<(ostream& os, const String& s);
    friend istream& operator>>(istream& is, const String& s);
    friend String operator+(const String& s1, const String& s2);
public:
    String(const char *str = ""){
        str_ = new char[strlen(str) + 1];
        strcpy(str_,str);
    }
    ~String(){
        delete[] str_;
    }

    char *str_;

    String(const String& other){
        delete[] str_;
        str_ = new char[strlen(other.str_)+1];
        strcpy(str_,other.str_);
    }

    String& operator=(const String& other){
        if(this != &other){
            delete[] str_;
            str_ = new char[strlen(other.str_) + 1];
            strcpy(str_,other.str_);
        }
        return *this;
    }

    String& operator=(const char *s){
        delete[] str_;
        str_ = new char[strlen(s) + 1];
        strcpy(str_,s);
        return *this;
    }

    String& operator+=(const String& other){
        char *str = new char[strlen(str_) + strlen(other.str_) + 1];
        strcpy(str,str_);
        strcat(str,other.str_);
        delete[] str_;
        str_ = str;
        return *this;
    }

    const char& operator[](int index) const{
        if(index >= strlen(str_)){
            throw out_of_range("index is out of range");
        }
        return str_[index];
    }

    char& operator[](int index){
        const char& c = (static_cast<const String&>(*this))[index];
        return const_cast<char &>(c);
    }

    
};

ostream& operator<<(ostream& os, const String& s){
    os<<s.str_;
    return os;
}

istream& operator>>(istream& is, String& s){
    char temp[1024];
    is>>temp;
    s = temp;
    return is;
}

String operator+(const String& s1, const String& s2){
    char * str = new char[strlen(s1.str_) + strlen(s2.str_) + 1];
    strcpy(str,s1.str_);
    strcat(str,s2.str_);
    return String(str);
}

int main(int argc, char const *argv[])
{
    String s1,s2;
    cin>>s1>>s2;
    s1 += s2;
    for(int i=0;i<strlen(s1.str_);i++){
        cout<<s1[i];
    }
    cout<<endl;
    String s3 = s1 + "!!!";
    cout<<s3;
    system("pause");
    return 0;
}

重载->运算符

这个运算符的重载有点奇怪,先看看下面的代码。

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

class DBHelper{
public:
    DBHelper(){
        db_ = new DB();
    }
    ~DBHelper(){
        delete db_;
    }
    DB* operator->(){
        return db_;
    }
private:
    DB* db_;
};

int main(int argc, char const *argv[])
{
    
    DBHelper db;
    db->open();
    db->close();
    return 0;
}

虽然->运算符返回了一个DB指针,但是它的后面可以直接跟上一个函数调用,这让我感觉有点奇怪。

重载operator new、operator delete

先看下面一段代码。

#include <iostream>
#include <string>
using namespace std;

int main(int argc, char const *argv[])
{
    char buf[1024];
    string *str = new(buf) string("hello");
    cout<<&buf<<" "<<str<<endl;
    str->~basic_string();
    return 0;
}

一个new操作符执行的操作是,operator new + 调用构造函数。operator new的主要作用是分配内存,这个操作符是可以重载的,并且有几种形式。上面的这种用法是placement new,不分配内存,而是指定一个内存。从上面的代码可以看出,在这种情况下,需要手动调用析构函数,不然构造函数中分配的内存无法释放。operator new和operator delete可以重载为局部的,也可以重载为全局的。我们可以通过重载一个全局的operator new和delete来实现一个简单的内存追踪器。内存跟踪器的大体思路是,我们重载全局的operator new和delete,自己实现内存的分配,然后使用一个全局的map,每次有内存分配,就添加一条记录;每次有释放内存,就去掉对应的记录。这个有点像模板设计模式,通过自定义一些功能来实现一些额外的功能。下面是具体的代码。

#include <map>
#include <iostream>

class Tracer{
    friend class Lock;
public:
    bool ready_;
    Tracer():lockCount_(0),ready_(true){}
    ~Tracer(){
        ready_ = false;
        dump();
    }
    void add(void *p,const char *file, unsigned int line){
        if(lockCount_ > 0){
            return;
        }
        Tracer::Lock lock(*this);
        entryMap_.insert(std::make_pair(p,Entry(file,line)));
    }

    void remove(void *p){
        if(lockCount_ > 0){
            return;
        }
        Tracer::Lock lock(*this);
        std::map<void*,Entry> :: const_iterator it = entryMap_.find(p);
        if(it != entryMap_.end()){
            entryMap_.erase(it);
        }
        
    }

    void dump(){
        if(entryMap_.size() > 0){
            std::cout << "-----------------------Memory leak(s)-----------------------" << std::endl;
            std::map<void*,Entry>::const_iterator it;
            for(it = entryMap_.begin();it != entryMap_.end(); ++it){
                std::cout << "0x" << std::hex << it->first << ":"
                    << it->second.file_ << "  line: " << std::dec << it->second.line_ << std::endl;
            }
            std::cout<<std::endl;
        }
    }
private:
    class  Entry{
    public:
        const char *file_;
        unsigned int line_;
        Entry(const char *file = NULL, unsigned int line = 0):file_(file),line_(line){}
    };

    class Lock{
    private:
        Tracer& tracer_;
    public:
        Lock(Tracer& tracer):tracer_(tracer){
            tracer_.lock();
        }
        ~Lock(){
            tracer_.unlock();
        }
    };
    unsigned int lockCount_;
    std::map<void*,Entry> entryMap_;
    void lock(){
        lockCount_++;
    }
    void unlock(){
        lockCount_--;
    }
};

Tracer gTracer;

void* operator new(size_t size, const char * file, unsigned int line){
    if(size == 0){
        size = 1;
    }
    void *p = malloc(size);
    if(gTracer.ready_ )
        gTracer.add(p,file,line);
    return p;
}
void operator delete(void *p, const char*, unsigned int line){
    if(gTracer.ready_ )
        gTracer.remove(p);
    free(p);
}

void* operator new(size_t size){
    if(size == 0){
        size = 1;
    }
    void *p = malloc(size);
    if(gTracer.ready_ )
        gTracer.add(p,"?",0);
    return p;
}

void operator delete(void *p){
    if(gTracer.ready_ )
        gTracer.remove(p);
    free(p);
}

void* operator new[](size_t size){
    if(size == 0){
        size = 1;
    }
    void *p = malloc(size);
    if(gTracer.ready_ )
        gTracer.add(p,"?",0);
    return p;
}

void operator delete[](void *p){
    if(gTracer.ready_ )
        gTracer.remove(p);
    free(p);
}

void* operator new[](size_t size, const char * file, unsigned int line){
    if(size == 0){
        size = 1;
    }
    void *p = malloc(size);
    if(gTracer.ready_ )
        gTracer.add(p,file,line);
    return p;
}

void operator delete[](void *p, const char * file, unsigned int line){
    if(gTracer.ready_ )
        gTracer.remove(p);
    free(p);
}

#define new new(__FILE__,__LINE__)

在构造函数中调用构造函数

class A{
public:
    A(int val):val_(val){
        cout<<val<<endl;
    }
    A(){
        new(this) A(10); //如果使用A(10)只会在栈上创建一个临时对象
    }
    int val_;
};
上一篇 下一篇

猜你喜欢

热点阅读