[c++] Operator overloading


Introduction


一、函數重載

關於重載 Overloading,最基本的是根據以下兩個特性:

 - 基於參數

 - 基於const

其實,函數重載也沒啥多余值得說的東西。

  

二、自定義操作規則

c++的操蛋屬性:自己為一檔,空一檔,其他隨意。

UB_stack a; UB_stack b = a; // copy
auto c = a; auto d {a};   // (or auto d = {a}), deduced type is std::initializer_list

這是一個抓狂的問題,詳見:http://scottmeyers.blogspot.com.au/2014/03/if-braced-initializers-have-no-type-why.html

Goto: C++11:std::initializer_list

 

大神的無奈

今日一樂:為何感覺到了Scott對chinese edition是黑白版本的好奇和無奈。

 

三、可重載 or 不可重載

Goto: C++ 重載運算符和重載函數

下面是:可重載的運算符列表

雙目算術運算符 + (加),-(減),*(乘),/(除),% (取模)
關系運算符 ==(等於),!= (不等於),< (小於),> (大於>,<=(小於等於),>=(大於等於)
邏輯運算符 ||(邏輯或),&&(邏輯與),!(邏輯非)
單目運算符 + (正),-(負),*(指針),&(取地址)
自增自減運算符 ++(自增),--(自減)
位運算符 | (按位或),& (按位與),~(按位取反),^(按位異或),,<< (左移),>>(右移)
賦值運算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空間申請與釋放 new, delete, new[ ] , delete[]
其他運算符 ()(函數調用),->(成員訪問),,(逗號),[](下標)

 

下面是:不可重載的運算符列表

成員訪問運算符 .
成員指針訪問運算符 .*, ->*
域運算符 ::
長度運算符 sizeof
條件運算符 ?
預處理符號 #

 

 

 

 

Overloaded Operator 


==

Ref: C++ 關系運算符重載

聲明關鍵字 operator,以及緊跟其后的一個c++預定義的操作符,舉例如下:

// 申明關鍵字
class
person{ private: int age; public: person(int a){ this->age=a; }
inline
bool operator == (const person &ps) const; };

// 實現方式如下 inline bool person::operator == (const person &ps) const { if (this->age==ps.age)  // 這里的this看上去是“符號”左邊的類 return true; return false; }

int main() { person p1(10); person p2(20); if(p1==p2) cout<<”the age is equal!”< return 0; }

 

 

>>,  <<,  +,  +=

包括:(1) 輸入輸出;(2) 自增自減;(3) 運算.

#include <iostream> #include <vector> #include <algorithm> #include <iterator> #include <UB_stack.h> using namespace std; class Test { public: Test(int x, int y):a{x},b{y}{} // The output operator must be defined as a friend function // and is usually a non-member function. // The input operator is similar.
    friend ostream& operator << (ostream&, const Test &); friend istream& operator >> (istream&, Test&); friend Test operator +(const Test&, const Test&);// Usually implemented as a member function.
    Test& operator += (const Test &); int returnA(void); void init(void); private: int a; int b; }; /******************************************************************************/

void Test::init(void) { this->a = 1; this->b = 1; } int Test::returnA(void) { return (this->a); } ostream& operator << (ostream &os, const Test &t) { os << t.a << " " << t.b << endl; } istream& operator >> (istream &is, Test &t) { is >> t.a >> t.b; } Test& Test::operator += (const Test &t) { this->a += t.a; this->b += t.b; return *this; } Test operator + (const Test &t1, const Test &t2) { Test ret = t1; ret += t2; return ret; } /******************************************************************************/

class SmallInt {
public: friend ostream& operator << (ostream &os, const SmallInt &s); friend bool operator < (const SmallInt&, const SmallInt&); SmallInt(int v): value_{v} {}
private: int value_; };
// friend function.
bool operator < (const SmallInt &rhs,const SmallInt &lhs) { return rhs.value_ <= lhs.value_; }
// friend function std::ostream
& operator<<(std::ostream &os, const SmallInt &s) { os << s.value_; return os; } /******************************************************************************/ int main() { cout << "Hello World!" << endl; Test t1{1, 2}; Test t2{10, 20}; /* * I/O Operators */ cout << t1 << t2; cin >> t2; cout << t1 << t2; t1.init(); t2.init(); /* * Compound Assignment Operators */ t2 += t1; cout << t2; /* * Arithmetic Operators */ cout << t1+t2; /* * Relational Operators * ... */ /* * Using the STL Sort and Copy Algorithms */ vector<SmallInt> vec{SmallInt{3}, SmallInt{1}, SmallInt{2}}; sort(vec.begin(), vec.end()); copy(vec.begin(), vec.end(), std::ostream_iterator<SmallInt>(std::cout, " ")); return 0; }

 

加法運算符重載 de 返回值

函數直接返回類,以為着什么?

會直接調用拷貝構造函數,然后默認實施的是:逐位拷貝語義.

Ref: C++進階系列:拷貝構造函數與NRV優化

Ref: 關於NRV優化[例子非常不粗]

請問從a, b傳入函數開始,一共創建了多少個對象?

Vector a, b;
Vector c = add(a, b);

 

操作符重載 與 友元函數

C++操作符重載形式——成員函數or友元函數

一般來說,C++運算符重載可采用成員函數和友元函數,二者都可以訪問類的私有成員,那么該采用哪一種呢?

(1)當重載為成員函數時,會隱含一個this指針;當重載為友元函數時,不存在隱含的this指針,需要在參數列表中顯示地添加操作數。

上述的代碼中,因為用了fridend函數,因此沒有用this,所以一元運算符重載 "用到了兩個參數".

(2)當重載為成員函數時,只允許右參數隱式轉換;當重載為友元函數時,能夠接受左參數和右參數的隱式轉換。

如果采用成員函數形式CString::operator+(const CString& rhs),則只能接受CString+char

如果執行char+CString則會編譯出錯。

簡單類型可以"隱式轉換"為復雜類型.

class CString
{
public:
    CString(char* str);
private:
    char* m_pStr;
};

 

  

一般而言,對於雙目運算符,最好將其重載為友元函數;而對於單目運算符,則最好重載為成員函數。

但是也存在例外情況。有些雙目運算符是不能重載為友元函數的,比如 賦值運算符=、函數調用運算符()、下標運算符[]、指針運算符-> 等,因為這些運算符在語義上與this都有太多的關聯。

比如=表示“將自身賦值為…”,[]表示“自己的第幾個元素”,如果將其重載為友元函數,則會出現語義上的不一致。

 

 

賦值運算符 =

實際操作當中,調用的是:拷貝構造函數。

返回void,只是”賦值“的簡單版本:https://www.runoob.com/cplusplus/assignment-operators-overloading.html 

 

 

函數調用運算符 ()

也叫做:functor

#include <stdio.h>
#include <string.h>
#include <thread>
#include <functional>
#include <iostream>
#include <stdio.h>
#include <algorithm>

using namespace std;
 
struct Sum_t
{
    Sum_t(int * t):total(t)
    {}; 

    int * total;

    void operator () (int element)
    {   
       *total+=element;
    }   
};

int main()
{
    int total = 0;
    Sum_t s(&total);  // <-- 這個是構造參數

    int arr[] = {0, 1, 2, 3, 4, 5}; 
 
    std::for_each(arr, arr+6, s);    // <-- 類作為函數來使用
    cout << total << endl;
}

 

  

下標運算符 [] 

假設 X 是某一個類的對象,類中定義了重載“[ ]”的 operator[ ] 函數,則表達式:

X[Y];
 可被解釋為:

X.operator[](Y);

定義實例:

class SafeArray {
public: SafeArray(int s); SafeArray(const int v[], int s); ~SafeArray() {delete[] values;} int& operator [] (int i); int operator [] (int i) const; private: int size; int *values; };
// 構造函數的實現 SafeArray::SafeArray(
int s) : size{s}, values{new int[size]} {} SafeArray::SafeArray(const int v[], int s) : size{s} { values = new int[size]; for (int i = 0; i < size; i++) { values[i] = v[i]; } }
// 符號重載的實現
int& SafeArray::operator [](int index) { assert((index >= 0) && (index < size)); return values[index]; } int SafeArray::operator [](int index) const {  // 常函數不能修改 函數內的成員,不能用於右賦值. assert((index >= 0) && (index < size)); return values[index]; }

使用樣例:

    SafeArray s{10}; // s[12] = 2; // 這算是兩個operator,因為是有賦值運算,必須返回&類型.
    cout << s[2] << endl;

 

 

自增自減符 ++, --

后綴法加了一個參數,有點意思。

From: https://www.runoob.com/cplusplus/increment-decrement-operators-overloading.html

#include <iostream>
using namespace std; class Time { private: int hours;             // 0 到 23
      int minutes;           // 0 到 59
public: // 所需的構造函數 Time(){ hours = 0; minutes = 0; } Time(int h, int m){ hours = h; minutes = m; }
// 顯示時間的方法 void displayTime() { cout << "H: " << hours << " M:" << minutes <<endl; }
// 重載前綴遞增運算符( ++x ) Time operator ++ () { ++minutes; // 對象加 1 if(minutes >= 60) { ++hours; minutes -= 60; } return Time(hours, minutes); }
// 重載后綴遞增運算符( x++ ) Time operator ++ (int) { // 保存原始值 Time T(hours, minutes); // 對象加 1 ++minutes; if(minutes >= 60) { ++hours; minutes -= 60; } // 返回舊的原始值 return T; } };

-------------------------------------------------------------
int main() { Time T1(11, 59), T2(10,40); ++T1; // T1 加 1 T1.displayTime(); // 顯示 T1 ++T1; // T1 再加 1 T1.displayTime(); // 顯示 T1 T2++; // T2 加 1 T2.displayTime(); // 顯示 T2 T2++; // T2 再加 1 T2.displayTime(); // 顯示 T2 return 0; }

 

 

指針相關的,比較復雜,但不經常用到的運算符重載.

 

  

指針運算符 -> , *

/* 感覺用處不是很大 */

Ref: C++ 類成員訪問運算符 -> 重載

#include <iostream>
#include <vector>
using namespace std;
 
// 假設一個實際的類
class Obj {
   static int i, j;
public:
   void f() const { cout << i++ << endl; }
   void g() const { cout << j++ << endl; }
};
 
// 靜態成員定義
int Obj::i = 10;
int Obj::j = 12;
 
// 為上面的類實現一個容器
class ObjContainer {
   vector<Obj*> a;
public:
   void add(Obj* obj)
   { 
      a.push_back(obj);  // 調用向量的標准方法
   }
   friend class SmartPointer;
};
 
// 實現智能指針,用於訪問類 Obj 的成員
class SmartPointer {
   ObjContainer oc;
   int index;
public:
   SmartPointer(ObjContainer& objc)
   { 
       oc = objc;
       index = 0;
   }
   // 返回值表示列表結束
   bool operator++() // 前綴版本
   { 
     if(index >= oc.a.size() - 1) return false;
     if(oc.a[++index] == 0) return false;
     return true;
   }
   bool operator++(int) // 后綴版本
   { 
      return operator++();
   }
   // 重載運算符 ->
   Obj* operator->() const 
   {
     if(!oc.a[index])
     {
        cout << "Zero value";
        return (Obj*)0;
     }
     return oc.a[index];
   }
};
 
int main() {
   const int sz = 10;
   Obj o[sz];
   ObjContainer oc;
   for(int i = 0; i < sz; i++)
   {
       oc.add(&o[i]);
   }
   SmartPointer sp(oc); // 創建一個迭代器
   do {
      sp->f(); // 智能指針調用
      sp->g();
   } while(sp++);
   return 0;
}
View Code

 

類成員訪問運算符( -> )可以被重載,但它較為麻煩。它被定義用於為一個類賦予"指針"行為。運算符 -> 必須是一個成員函數。如果使用了 -> 運算符,返回類型必須是指針或者是類的對象。

運算符 -> 通常與指針引用運算符 * 結合使用,用於實現"智能指針"的功能。這些指針是行為與正常指針相似的對象,唯一不同的是,當您通過指針訪問對象時,它們會執行其他的任務。比如,當指針銷毀時,或者當指針指向另一個對象時,會自動刪除對象。

-> must be a member function and * is usually a member.

(Here 簡介)

貌似不錯的智能指針的博文:http://www.cnblogs.com/lanxuezaipiao/p/4132096.html

The Basic Idea Behind All Smart Pointers

 

 

類型轉換操作符

Type Conversion Operators

Conversion operators must be defined as member functions. They
do not take any parameters, nor do they specify a return type.
Typically conversions don’t modify the object and are declared
const.

 

如果去掉explicit(顯式的),則User code中的line 2即可成立。

explicit: 聲明為explicit的構造函數,不能在隱式轉換中使用。

 

End.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM