重載 operator+,+= 符號
tags: C++ Concept
如何合併+
與+=
的代碼,避免寫兩份一樣的代碼
類別的定義
class Arr{
public:
Arr(size_t len=3, int value=1): num(len) {...}
vector<int> num;
};
1. 常數項無法呼叫函式成員
首先如果把+寫進成員函式,讓他與常數做相加會遇到一些問題。
Arr & Arr::operator+(int value){...}
Arr p1;
p1=p1+1; // ok
p1=1+p1; // error
問題在於
所以必須把她寫在外面來用全域函式
常數沒辦法呼叫成員函式 operator+
所以必須把她寫在外面來用全域函式
Arr operator+(int value, Arr const &rhs){...}
Arr operator+(Arr const &lhs, int value){...}
如此一來就可以任意將常數放置在前與後了。
注意
如果你把該函式放置在底下,會找不到該函式的定義,在類別宣告內加入
friend Arr operator+(Arr const &lhs, Arr const &rhs);
2. + 多建一個物件
觀察一下程式代碼 + 通常都是帶等於的
Arr p1, p2;
p1 = p1+p2;
p1+p2; // 不會出錯但貌似沒作用...
可以推論出
- 你不能改變 p1 與 p2
- 你必須返回他們相加的結果
這會導致一個問題
Arr operator+(Arr const &lhs, Arr const &rhs){...}
裡面的
lhs
rhs
你都不能動他,但又要返回一個新值你只能在裡面創一個暫存用來接收他們的相加值
Arr operator+(Arr const &lhs, Arr const &rhs){
Arr temp;
}
又物件的建立與複製非常消耗效能
你會在函式內建構一個新的,然後在賦值
Arr temp;
temp.num[i]=lhs.num[i]+rhs.num[i];
到了外面主程式又複製一次
然後會解構兩次p1
一次是函式內的暫存,一次是函式傳出的
p1 = p1+p2;
如果有三個會解構4次p1
,因為每次只能2個2個來(每個2次)
補充效能問題
從以上可以觀察到,這樣子的代碼會比較拖效能
(多呼叫一次=的複製函式,還有p1會解構2次)
(多呼叫一次=的複製函式,還有p1會解構2次)
p1 = p1+p2;
這樣的子的代碼能夠比較節省效能
p1 += p2;
所以如果有多項要相加
p1 += p2;
p1 += p3;
代碼拆多一點,效能會好一點
實測p=p1+p2+p3+p4;
會解構6次p1
,拆開寫0次
3. 合併 +, += 代碼
為了避免物件建立後才給值的狀況你可以依靠
同時也可以合併
+=
來規避同時也可以合併
+
與+=
的代碼,使他成為一份主代碼(才不用改2次)+=
本身並不會多複製物件
Arr & Arr::operator+=(Arr const &rhs){
for(unsigned i = 0; i < num.size(); ++i)
this->num[i] += rhs.num[i];
return *this;
}
讓
(注意這裡的+是全域函式)
operator+
去呼叫 +=
(注意這裡的+是全域函式)
Arr operator+(Arr const &lhs, Arr const &rhs){
cout << "lhs+rhs : ";
return Arr(lhs) += rhs;
}
這樣做會函式內會優化成直接建構一個有初值的物件;
而不是先建構,建好了再賦值。
已知條件
operator+
會產生暫存值(複製行為)operator+=
不會產生暫存直(沒有複製行為)
如果你讓
兩個都有複製行為了,不應該如此浪費效能才是。
operator+=
去呼叫 operator+
是不是就變成兩個都有複製行為了,不應該如此浪費效能才是。
參考代碼
/*****************************************************************
Name : 重載 operator+ 與 += 函式
Date : 2017/02/22
By : CharlotteHonG
Final: 2017/03/05
*****************************************************************/
#include <iostream>
#include <vector>
using namespace std;
class Arr{
public:
Arr(size_t len=3, int value=1): num(len) {
// cout << "Constructor" << endl;
for(auto&& i : num)
i=value;
}
~Arr(){
// cout << "Destructor" << endl;
}
void pri(){
for(auto&& i : num) {
cout << i << ", ";
}cout << endl;
}
public:
Arr & operator+=(Arr const &rhs){
for(unsigned i = 0; i < num.size(); ++i)
this->num[i] += rhs.num[i];
return *this;
}
Arr & operator+=(int value){
for(unsigned i = 0; i < num.size(); ++i)
this->num[i] += value;
return *this;
}
private:
vector<int> num;
};
Arr operator+(Arr const &lhs, Arr const &rhs){
cout << "lhs+rhs : ";
return Arr(lhs) += rhs;
}
Arr operator+(int value, Arr const &rhs){
cout << "rhs+ : ";
return Arr(rhs) += value;
}
Arr operator+(Arr const &lhs, int value){
cout << "lhs+ : ";
return Arr(lhs) += value;
}
/*==============================================================*/
int main(int argc, char const *argv[]){
Arr p;
p=p+p;
p.pri();
p=1+p;
p.pri();
p=p+1;
p.pri();
return 0;
}
/*==============================================================*/
更新 20190511
關於這一份代碼
Arr operator+(Arr const &lhs, Arr const &rhs){
cout << "lhs+rhs : ";
return Arr(lhs) += rhs;
}
參考自 cpp ref 網站的 type 是
https://en.cppreference.com/w/cpp/language/operators
https://en.cppreference.com/w/cpp/language/operators
Arr operator+(Arr lhs, Arr const &rhs){
cout << "lhs+rhs : ";
return lhs += rhs;
}
兩個是等價的
上面的是把copy寫在Return的時候,那時候才創建一個新類別並複製。
下面的是傳入的時候就發生copy,那時候就創建一個新類別了。
上面的是把copy寫在Return的時候,那時候才創建一個新類別並複製。
下面的是傳入的時候就發生copy,那時候就創建一個新類別了。
我建議你用下面的版本,因為那是官方給的XDD
能想到的差別是,如果你的op+不是常規的方式,有自訂什麼特別功能,不像文中的功能只是呼叫op+=()就完事了,在定義內有複雜的運算,這時候上面的寫法,lhs這個變數就不能動他,因為傳進來的時候帶const,這會造成很大困擾的歐!
能想到的差別是,如果你的op+不是常規的方式,有自訂什麼特別功能,不像文中的功能只是呼叫op+=()就完事了,在定義內有複雜的運算,這時候上面的寫法,lhs這個變數就不能動他,因為傳進來的時候帶const,這會造成很大困擾的歐!
如果是常規狀態,如文內範例,就是等價的沒什麼差~
內文就不修改了,用補充的方式添加,修改還要校正QQ
沒校正怕哪邊失誤多一個字少一個字之類的出錯~~
內文就不修改了,用補充的方式添加,修改還要校正QQ
沒校正怕哪邊失誤多一個字少一個字之類的出錯~~