1. 程式人生 > >C/C++基礎----過載運算與型別轉換

C/C++基礎----過載運算與型別轉換

  • 非成員版本

    data1 + data2;
    operator+(data1, data2);

  • 成員版本

    data1 += data2;
    data1.operator+=(data2);

  • 不建議的過載

邏輯與、邏輯或、逗號的運算物件求值順序規則無法保留。
&&和||的過載版本也沒法保留內建運算子的短路求值屬性,兩個運算物件總是會被求值。
逗號和取址,已經在C++中定義了其用於類物件是的特殊含義,已經有了內建的含義,一般不應該過載。
  • 有些運算子必須作為成員,有些則作為普通函式更好
    1. 賦值= 下標[] 呼叫() 成員訪問箭頭->必須是成員
    2. 複合賦值一般應該是成員,但並非必須
    3. 改變物件狀態的運算子或者與給定型別密切相關的運算子,如++,--,解引用通常是成員
    4. 具有對稱性的,可能轉換任意一端的運算子物件,如算術、相等性、關係和位運算,通常是非成員。
  • 輸出運算子<<
ostream &operator<<(ostream &os, const Sales_data &item)
輸出運算子不太考慮格式化操作,使使用者有權控制輸出細節
與iostream標準庫相容的輸入輸出運算子必須是非成員函式
  • 輸入運算子>>
istream &operator>>(istream &is, Sales_data &item)
輸入運算子必須處理可能失敗的情況(資料型別錯誤,到底檔案尾或遇到輸入流其他錯誤)
  • 算術和關係
通常定義為非成員,通常不需要改變運算物件(常量引用),計算得到一個新值。
如定義了複合賦值,最有效的是使用複合賦值來定義算術運算。
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)

bool operator==(const Sales_data &lhs, cosnt Sales_data &rhs)
bool operator!=(const Sales_data &lhs, cosnt Sales_data &rhs)
  • 關係運算符
1 定義順序關係,與關聯容器對關鍵字的要求一致(唯一性、傳遞性、等價性)
2 如果同時含有==的話,則定義一種關係令其與==保持一致
當存在一種唯一可靠的<定義,且和==產生的結果一致時,才定義<
  • 賦值運算子
StrVec &StrVec::operator=(initializer_list<string> il)
Sales_data &Sales_data::operater+=(const Sales_data &rhs)
賦值運算子必須是成員函式,複合賦值也通常定義為成員函式。
一般算術運算呼叫複合賦值,可讀寫較好
  • 下標運算
通常返回引用,最好同時定義常量和非常量版本,必須成員函式
std::string &operator[](std::size_t n) {return elements[n];}
const std::string &operator[](std::size_t n) const {return elements[n];}
  • 遞增遞減
StrBlobPtr& StrBlobPtr::operator++()    前置版本
StrBlobPtr StrBlobPtr::operator++(int)    後置版本
後置可以呼叫前置來完成,前置版本需檢查遞增操作的有效性。一般設定為成員函式。
  • 成員訪問
class StrBlob{
public:
std::string &operator*() const
{   auto p = check(curr, “dereference past end”;
    return (*p)[curr];
}
std::string *operator->() const
{
    return & this->operator*();
}
}
箭頭必須成員,解引用也通常成員
箭頭運算子永遠不能丟掉成員訪問的基本含義
point->mem
point必須是指向類物件的指標或者是過載了operator->的類物件
1是指標,等價於(*point).mem
2是物件,呼叫point.operator->()的結果來獲取mem。如果返回的是指標則執行第1步;如果返回的結果本身過載了->,則重複呼叫。或者返回錯誤。
  • 呼叫運算子
函式物件,同時也能儲存狀態,比普通函式更靈活
同一個物件裡可以過載好幾個不同版本的呼叫函式,同時可以改變資料成員來定製不同操作。隱含的this引數呢???看呼叫的形式
  • lambda是未命名類的未命名物件
預設情況下,是一個const成員函式,不能改變它捕獲的變數。顯式宣告為mutable則不是。
產生的類不含預設建構函式、賦值運算子及預設解構函式??
是否含有預設的拷貝/移動構造要視捕獲的資料型別而定。
  • 標準庫定義的函式物件
算術 關係 邏輯
plus equal_to logical_and
minus not_equal_to logical_or
multiplies greater logical_not
divides greater_equal
modulus less
negate less_equal
常用來替換演算法中的預設運算子,這些函式物件對指標同樣適用。
sort(a.beg,a.end, less<string*>() );  //正確
而用<,則將產生未定義的行為

關聯容器使用less<key_type>對元素排序,可以定義一個指標作為關鍵字的set或map而無須直接宣告less
  • 可呼叫物件
函式、函式指標、lambda表示式、bind建立的物件、過載了函式呼叫運算子的類。
fun &fun和funP列印的地址是一樣的。funP可以被賦值,而fun不可以。有兩種解釋
1函式名與FunP函式指標都是函式指標。fun是一個函式指標常量,funP是一個函式數指標變數。
2函式名和陣列名實際上都不是指標,但是,在使用時可以退化成指標,即編譯器可以幫助我們實現自動的轉換。

既然都是都有指標的效果,為什麼要定義函式指標?
二義性問題,如有幾個版本add函式,不知道哪個?
起到一定的封裝效果,可以提供統一介面。C++虛擬函式表就是通過函式指標實現。

不同型別呼叫物件可能共享同一種呼叫形式
map<string, int(*)(int, int)> binops;能存函式指標,存不了函式物件和lambda

function類 <functional>
可以接受同調用型別的可呼叫物件
function<int (int, int)> f1=add;

map<string,function<int(int,int)>> binops = {
    {“+”, add},
    {“-”, std::minus<int>()} };
binops[“+”](10, 5);   //呼叫add(10, 5)

型別轉換可以面向任何可以作為返回型別的型別,不允許轉換成資料或函式型別。,必須定位為成員函式,通常const。
編譯器只能進行一個使用者定義的型別轉換,但是隱式地使用者定義型別轉換可以置於一個標準(內建)型別轉換之前或之後。
explicit operator int() const {return val;}
static_cast<int>(si)+3;
一個例外,當用作條件時,編譯器會將顯式的型別轉換自動應用於它。
if while do  for  與或非 ?:
  • 避免二義性
兩個類提供相同的型別轉換
類定義了多個轉換規則
當使用使用者定義的型別轉換時,如果包含標準型別轉換,轉換的級別決定了最佳匹配選擇