2017年5月5日 星期五

operator 運算子的各式標準寫法與原因

operator 運算子的各式標準寫法與原因

tags: C++ Concept2
operator存在著許多應該遵守的公約,這裡會列出常見的operator如何撰寫標準公約,並說明為何要如此撰寫與舉例


宣告

/* 那些只有一行的函式可以直接上inline */

// +運算子
Arr & operator+=(Arr const &rhs);
friend Arr const operator+(Arr const &lhs, Arr const &rhs);
    return Arr(lhs) += rhs; // op+的定義

// 下標運算子
int & operator[](size_t idx){
    return const_cast<int&>(static_cast<const Arr&>(*this)[idx]);
}
const int & operator[](size_t idx) const;

// <<運算符號
friend ostream & operator<< (ostream& s, const Rati & r);

// 複製建構子
List(List const & rhs);
// 複製函式
List & operator=(List const & rhs);

// 移動建構子
List(List && rhs){
    (*this) = std::move(rhs);
}
// 移動函式
List & operator=(List && rhs);

// 取址
T* operator&();
// 取值(轉型)
operator T&();

// ++T
Arr & operator++();
// T++
Arr operator++(int);


公約

盡可能記住他,在大多數的可以幫你避開很多坑

參數

看一下以下的例子,這樣的寫法可以同時接收const與non-const
void fun(int const & num){
    cout << "num=" << num << endl;
}

int i=0;
fun(i);
  1. 避免寫兩個 fun() 函式
  2. 不小心修改到時編譯器會提醒你
參數能夠加上 const 就加上 const

返回的物件

假設一個沒有被加上const物件被返回可能會發生這種事情
Arr operator+(Arr const &lhs, Arr const &rhs);

Arr a, b;
a+a=b;
或許你不會這寫,但不能保證沒有人會這樣寫,嘗試對一個即將被解構的物件修改,其結果只是浪費效能。
返回的物件能夠加上 const 就加上 const

返回 (*this) 的參考

我們應該效仿基本的型別操作,int這些基礎型別是可以連續被指定的
int a, b, c, d;
a=b=c=d=1;
我們只要傳出 (*this) 的參考就可以做到這樣的操作,可以把他們拆解成原本的樣態,可以更容易理解
Arr a, b, c;
a=b=c;

a.operator=(b.operator=(c));

適當的使用全域函式

當我們使用 op+() 的時候要注意一種情況
Arr a;
a+1;
這把它還原看起來沒問題
a.operator+(1);
可是反過來就出問題
1.operator+(a);
這可以使用全域函式處理
Arr const operator+(int lhs, Arr const &rhs);
Arr const operator+(Arr const &lhs, int rhs);

適當的引用重複的函式

可以觀察到某些函式其實是具有重複性的
  • op+() 可以拆解為兩個已有的函式,複製後+=
  • 下標負號的 const 與 non-const
  • 移動建構子的內容與移動函式相等
  • 複製建構子與複製函式部分相同(額外拆一個函式呼叫)

拆解

一個相加的函式可以拆解為
int a=1, b=2;

int temp=a;
temp+=b;
下標符號比較特別需要特別為了 const 與 non-const 寫兩個完全一樣的函式
int & operator[](size_t idx);
const int & operator[](size_t idx) const;

Arr a;
const Arr b;

a[0]=1;
b[0];
解決方法長這個樣子
int & operator[](size_t idx){
    return const_cast<int&>(static_cast<const Arr&>(*this)[idx]);
}
簡單來說從右邊看回來
  1. 先加上const屬性
  2. 然後讓它去呼叫const的op[]()函式
  3. 再強制解除他的 const 屬性
順序不要反過來了把主代碼寫在 non-const (是可以正常運行的)
讓 const 去呼叫 non-const 的函式
這樣會導致函式運行的時候 const 屬性被解除可以被修改。
難保後面的人沒察覺到…(通常這個人是幾個月後的自己)

補上 friend 開放 private 的存取權限

某些函式寫在全域函式會比寫在成員函式還要來的好,可是一旦寫到全域函式去之後,相對的就會失去對private成員的存取權限
在類別的定義裡面補上該全域函式的 friend宣告,讓全域函式可以有應有的權限操作成員函式

待續

沒有留言:

張貼留言