[C++学习笔记08]操作符重载

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

  2. 成员函数重载  
      成员函数原型的格式:      
        函数类型 operator 运算符(参数表);
      
      成员函数定义的格式:     
        函数类型 类名::operator 运算符(参数表) {        
          函数体;

        }

      一个复数+运算符以成员函数重载的例子
    class Complex {
    public:
        Complex(int real, int imag) : real_(real), imag_(imag)
        {
    
        }
    
        Complex operator+(const Complex &other)
        {
            int r = real_ + other.real_;
            int i = imag_ + other.imag_;
    
            return Complex(r, i);
        }
    
        void display()
        {
            cout << real_ << "+" << imag_ << "i" << endl;
        }
    private:
        int real_;
        int imag_;
    };
    
    int main(void)
    {
        Complex c1(1, 2);
        Complex c2(2, 3);
    
        Complex c3 = c1 + c2; // 等价于c3 = c1.operator+(c2)
    
        c3.display();
    
        return 0;
    }

  3. 非成员函数重载  
      友元函数原型的格式:      
        friend 函数类型 operator 运算符(参数表);  
      友元函数定义的格式: 
        函数类型 operator 运算符(参数表){ 
          函数体; 
        }
    一个复数+运算符以有元函数重载的例子
    class Complex {
        friend Complex operator+(const Complex &c1, const Complex &c2);
    public:
        Complex(int real, int imag) : real_(real), imag_(imag)
        {
    
        }
    
        void display()
        {
            cout << real_ << "+" << imag_ << "i" << endl;
        }
    private:
        int real_;
        int imag_;
    };
    
    Complex operator+(const Complex &c1, const Complex &c2)
    {
        int r = c1.real_ + c2.real_;
        int i = c1.imag_ + c2.imag_;
    
        return Complex(r, i);
    }
    
    int main(void)
    {
        Complex c1(1, 2);
        Complex c2(2, 3);
    
        Complex c3 = c1 + c2;
    
        c3.display();
    
        return 0;
    }

  4. 运算符重载规则
      运算符重载不允许发明新的运算符。
      不能改变运算符操作对象的个数。
      运算符被重载后,其优先级和结合性不会改变。
      不能重载的运算符:
         作用域解析运算符::),条件运算符?:),直接成员访问运算符.),类成员指针引用的运算符.*
    ),sizeof运算符
      一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
      以下一些双目运算符不能重载为类的友元函数=(比如把基类赋值给基类)、()、[]、->
      类型转换运算符只能以成员函数方式重载
      流运算符只能以友元的方式重载
  5. ++运算符重载
      前置++运算符重载    
        成员函数的方式重载,原型为: 
          函数类型 & operator++();
        友元函数的方式重载,原型为: 
          friend 函数类型 & operator++(类类型 &);  
      
    后置++运算符重载    
        成员函数的方式重载,原型为: 
          函数类型 & operator++(int);
        友元函数的方式重载,原型为:   
          friend函数类型 & operator++(类类型 &,int);
      
    案例代码
      
    class Integer{
    public:
        Integer(int num = 0) : num_(num)
        {
    
        }
    
        // 前置++
        /*Integer & operator++()
        {
            ++num_;
            return *this;
        }*/
        // 这里有元重载和成员函数重载不能共存,不像+
        friend Integer & operator++(Integer &it)
        {
            ++it.num_;
            return it;
        }
    
        // 后置++,返回对象,非引用
        /*Integer operator++(int i)
        {
            Integer it(num_);
            num_++;
            return it;
        }*/
    
        friend Integer operator++(Integer &it, int i)
        {
            Integer itm(it.num_);
            it.num_++;
            return itm;
        }
    
        void display()
        {
            cout << num_ << endl;
        }
    private:
        int num_;
    };
    
    int main(void)
    {
        Integer it1(10);
        Integer it2 = ++it1;
        it1.display(); // 11
        it2.display(); // 11
    
        Integer it3 = it1++;
        it1.display(); // 12
        it3.display(); // 11
    
        return 0;
    }

  6. !运算符重载
      一个String类中!运算符重载的实现
    bool operator!()
    {
        return strlen(str_) == 0;
    }
    
    friend bool operator!(String &st)
    {
        return strlen(st.str_) == 0;
    }

  7. 赋值运算符重载
      一个String类中=重载的实现
    class String {
    public:
        explicit String(const char *str="")
        {
            str_ = allocAndCpy(str);
        }
    
        String(const String &str)
        {
            str_ = allocAndCpy(str.str_);
        }
    
        char *allocAndCpy(const char *str)
        {
            char *s = NULL;
            int len = strlen(str) + 1;
            s = new char[len];
            memset(s, 0, len);
            strcpy(s, str);
            return s;
        }
    
        bool operator!()
        {
            return strlen(str_) == 0;
        }
    
        friend bool operator!(String &st)
        {
            return strlen(st.str_) == 0;
        }
    
        String& operator=(const String &other)
        {
            if (this == &other)
                return *this;
    
            delete[]str_;
            str_ = allocAndCpy(other.str_);
            return *this;
        }
    
        String& operator=(const char *str)
        {
            delete[]str_;
            str_ = allocAndCpy(str);
            return *this;
        }
    
        void display()
        {
            cout << str_ << endl;
        }
    
        ~String()
        {
            delete[]str_;
        }
    private:
        char *str_;
    };
    
    int main(void)
    {
        String st("AAAAA");
    
        String st2;
        st2 = st;
        st2.display();
        st2 = "BBBB"; // 没在构造函数前加explict前,会调用构造函数,生成临时对象,
                    // 加上explicit关键字,重载=(const char *str)即可
        st2.display();
    
        return 0;
    }

  8.  String类实现
      
    []运算符重载-必须重载成成员函数
      +运算符重载-使用+=运算符重载优化代码
      +=运算符重载
      <<运算符重载-必须重载成有元函数
      >>运算符重载-必须重载成有元函数
      实现代码
      String.h:
    // String.h
    
    #ifndef __STRING_H__
    #define __STRING_H__
    #include <iostream>
    using namespace std;
    
    class String {
    public:
        explicit String(const char *str = "");
        String(const String &str);
        char *allocAndCpy(const char *str) const;
        bool operator!() const;
        friend bool operator!(String &st);
        String &operator=(const String &other);
        String &operator=(const char *str);
        void display() const;
        char &operator[](int index) const;
        //friend char &operator[](String &st, int index); // 不能把[]以有元重载
        //String operator+(const String &other) const;
        friend String operator+(const String &st, const String &other);
        //String &operator+=(const String &other);
        friend String &operator+=(String &st, const String &other);
        friend ostream &operator<<(ostream &os, const String &st);
        friend istream &operator>>(istream &is, String &st);
        ~String();
    private:
        char *str_;
    };
    
    #endif // __STRING_H__

     String.cpp:

    // String.cpp
    
    #define _CRT_SECURE_NO_WARNINGS
    #include "String.h"
    #include <cstring>
    
    String::String(const char *str)
    {
        str_ = allocAndCpy(str);
    }
    
    String::String(const String &str)
    {
        str_ = allocAndCpy(str.str_);
    }
    
    char *String::allocAndCpy(const char *str) const
    {
        char *s = NULL;
        int len = strlen(str) + 1;
        s = new char[len];
        memset(s, 0, len);
        strcpy(s, str);
        return s;
    }
    
    bool String::operator!() const
    {
        return strlen(str_) == 0;
    }
    
    bool operator!(String &st)
    {
        return strlen(st.str_) == 0;
    }
    
    String& String::operator=(const String &other)
    {
        if (this == &other)
            return *this;
    
        delete[]str_;
        str_ = allocAndCpy(other.str_);
        return *this;
    }
    
    String& String::operator=(const char *str)
    {
        delete[]str_;
        str_ = allocAndCpy(str);
        return *this;
    }
    
    void String::display() const
    {
        cout << str_ << endl;
    }
    
    // 返回引用,给函数赋值,比如a[0] = 10;
    char &String::operator[](int index) const
    {
        return str_[index];
    }
    
    //String String::operator+(const String &other) const
    //{
    //    int len = strlen(str_) + strlen(other.str_) + 1;
    //    char *newstr = new char[len];
    //    strcpy(newstr, str_);
    //    strcat(newstr, other.str_);
    //    String tmp(newstr);
    //    delete[]newstr;
    //
    //    return tmp;
    //}
    
    // 以有元方式重载+,当成员函数重载也存在,并且有元的参数为const,那么+默认调用有元方式重载
    // 一旦加了const,那么就调用不明确了
    String operator+(const String &st, const String &other)
    {
        /*int len = strlen(st.str_) + strlen(other.str_) + 1;
        char *newstr = new char[len];
        strcpy(newstr, st.str_);
        strcat(newstr, other.str_);
        String tmp(newstr);
        delete[]newstr;*/
        // 优化直接调用+=
        String tmp(st);
        tmp += other;
    
        return tmp;
    }
    
    //String &String::operator += (const String &other)
    //{
    //    int len = strlen(str_) + strlen(other.str_) + 1;
    //    char *newstr = new char[len];
    //    strcpy(newstr, str_);
    //    strcat(newstr, other.str_);
    //    str_ = newstr;
    //
    //    return *this;
    //}
    
    String &operator+=(String &st, const String &other)
    {
        int len = strlen(st.str_) + strlen(other.str_) + 1;
        char *newstr = new char[len];
        strcpy(newstr, st.str_);
        strcat(newstr, other.str_);
        st.str_ = newstr;
    
        return st;
    }
    
    ostream &operator << (ostream &os, const String &st)
    {
        os << st.str_ << endl;
        return os;
    }
    
    istream &operator>>(istream &is, String &st)
    {
        char tmp[1024];
        cin >> tmp;
        st = tmp;
        return is;
    }
    
    String::~String()
    {
        delete[]str_;
    }

    测试代码:

    // 测试代码
    
    #define _CRT_SECURE_NO_WARNINGS
    
    #include "String.h"
    #include <cstring>
    
    int main(void)
    {
        String st("AAAAA");
    
        String st2;
        st2 = st;
        st2.display();
        st2 = "BBBB"; // 没在构造函数前加explict前,会调用构造函数,生成临时对象,
        // 加上explicit关键字,重载=(const char *str)即可
        st2.display();
    
        // 重载[]
        cout << st[2] << endl;
        st[2] = 'C';
        st.display();
    
        // 重载+
        String st3 = st + st2;
        st3.display();
    
        // 重载+=
        st += st2;
        st.display();
    
        // 重载<<
        cout << st << endl;
    
        // 重载<<
        cin >> st;
        cout << st << endl;
    
        return 0;
    }

  9. 类型转换运算符重载
      必须是成员函数,不能是友元函数
      没有参数(操作数是什么?)
      不能指定返回类型(其实已经指定了)
      函数原型:operator 类型名();

      
    一个Integer类转换成int类型的类型转换运算符的重载:
      Integer.h:
    #ifndef _INTEGER_H_
    #define _INTEGER_H_
    
    class Integer
    {
    public:
        Integer(int n);
        ~Integer();
    
        Integer& operator++();
        //friend Integer& operator++(Integer& i);
    
        Integer operator++(int n);
        //friend Integer operator++(Integer& i, int n);
    
        operator int();
    
        void Display() const;
    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 ++()
    {
        //cout<<"Integer& Integer::operator ++()"<<endl;
        ++n_;
        return *this;
    }
    
    //Integer& operator++(Integer& i)
    //{
    //    //cout<<"Integer& operator++(Integer& i)"<<endl;
    //    ++i.n_;
    //    return i;
    //}
    
    Integer Integer::operator++(int n)
    {
        //cout<<"Integer& Integer::operator ++()"<<endl;
        //n_++;
        Integer tmp(n_);
        n_++;
        return tmp;
    }
    
    //Integer operator++(Integer& i, int n)
    //{
    //    Integer tmp(i.n_);
    //    i.n_++;
    //    return tmp;
    //}
    
    Integer::operator int()
    {
        return n_;
    }
    
    void Integer::Display() const
    {
        cout<<n_<<endl;
    }

      测试代码:

    #include "Integer.h"
    #include <iostream>
    using namespace std;
    
    int add(int a, int b)
    {
        return a + b;
    }
    
    int main(void)
    {
        Integer n(100);
        n = 200;
        n.Display();
    
        int sum = add(n, 100);
    
        cout<<sum<<endl;
    
        int x = n;
        int y = static_cast<int>(n);
    
        return 0;
    }

  10. ->运算符重载
      
    (通过栈上的对象控制堆上的对象的内存释放)
    class DBHelper {
    public:
        DBHelper()
        {
            cout << "DBHelper() ... " << endl;
        }
        ~DBHelper()
        {
            cout << "~DBHelper() ... " << endl;
        }
        void Open()
        {
            cout << "Open() ... " << endl;
        }
        void Close()
        {
            cout << "Close() ... " << endl;
        }
        void Query()
        {
            cout << "Query() ... " << endl;
        }
    private:
        int num_;
    };
    
    class DB {
    public:
        DB()
        {
            cout << "DB() ..." << endl;
            helper_ = new DBHelper;
        }
        ~DB()
        {
            cout << "~DB() ..." << endl;
            delete helper_;
        }
        // 重载->实现DB对象析构时自动释放DBHelper的空间
        DBHelper* operator->()
        {
            return helper_;
        }
    
    private:
        DBHelper *helper_;
    };
    
    int main(void)
    {
        // 一旦重载了->,对象也能使用->操作了
        // 有点智能指针的影子,通过栈上的对象去控制堆上的对象
        DB db;
        db->Open();
        db->Query();
        db->Close();
    
        return 0;
    }

     

  11. operator new、operator delete、placement new 深入
    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    class Test {
    public:
        Test(int num = 0) : num_(num)
        {
            cout << "Test(int num = 0) ..." << endl;
        }
        ~Test()
        {
            cout << "~Test() ... " << endl;
        }
    
        // 1.operator new
        void *operator new (size_t size)
        {
            Test *p = (Test *)malloc(size);
            return p;
        }
    
        // 2.placement new 
        void *operator new (size_t size, void *p)
        {
            return p;
        }
    
        // 3.三个参数的new
        void *operator new (size_t size, const char *file, long line)
        {
            Test *p = (Test *)malloc(size);
            cout << "file:" << file << "-" << line << " line" << endl; 
            return p;
        }
    
        void *operator new[](size_t size)
        {
            Test *p = (Test *)malloc(size);
            return p;
        }
    
        // 1.operator delete
        void operator delete (void *p) // 默认调用,要或者释放了多少个字节,可以重载为下面的函数
        {
            free(p);
        }
        // 1.operator delete
        void operator delete (void *p, size_t size)
        {
            free(p);
            cout << "size=" << size << endl;
        }
    
        // 2.placement delete when 当placement new初始化引发异常
        void operator delete (void *, void *)
        {
            // do nothing
        }
    
        // 3.三个参数的delete
        void operator delete (void *p, const char *file, long line)
        {
            free(p);
        }
    
        // 根据以上,还可以有四个参数的delete
        /*void operator delete (void *p, size_t size, const char *file, long line)
        {
            free(p);
            cout << "4 par: size=" << size << endl;
        }*/
    
        int num_;
    };
    
    void *operator new[] (size_t size)
    {
        int *p = (int *)malloc(size);
        return p;
        //Test *p = (Test *)malloc(size);
        //return p;
    }
    
    // 一个参数delete[]
    void operator delete[](void *p)
    {
        free(p);
    }
    
    // 两个参数的delete[],一样只是想得到析构的字节数,貌似就算注释掉一个参数的delete[]也不会调用这个两个参数的
    //void operator delete[](void *p, size_t size)
    //{
    //    free(p);
    //    cout << "[] size=" << size << endl;
    //}
    
    int main(void)
    {
        Test *t1 = new Test; // new operator = operator new + 构造函数
        delete t1; // delete operator = operator delete + 析构函数
        
        char buf[10]; // 分配内存不使用
        Test *t2 = new (buf)Test(10); // placement new = operator new + 构造函数,调用 void * operator new(size_t, void *_Where)类型的函数
        Test *p = reinterpret_cast<Test *>(buf); // 或者使用传统的强制转换
        //Test *q = static_cast<Test *>(buf); // static_cast转换无效
        cout << p->num_ << endl;    // 10, 在buf的内存空间上来保存*t2
        p->~Test(); // 手动调用析构函数的原因是,因为placement new会自动调用构造函数肯能分配了内存,必须调用析构函数释放
        // delete []t2; // 本身就是栈上的对象不需要去释放,强制delete直接报错
    
        Test *t3 = new (__FILE__, __LINE__) Test(10); // 检测哪个文件哪一行分配了内存,调用3个参数的new
        delete t3;
    
        // new []  delete[] 重载部分,使用全局的来重载,仅仅是简单演示
        int *p1 = new int[10]; // 
        delete[]p1;
    
        /*Test *t4 = new Test[10];
        delete[]t4;*/ // 当全局也重载了,去调用成员函数中的new []
    
        return 0;
    }

      

posted @ 2015-05-19 22:18  IFPELSET  阅读(231)  评论(0编辑  收藏  举报