2017年2月23日 星期四

C++ 重載 operator + , += 重複打兩次及效能問題

重載 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; // 不會出錯但貌似沒作用...
可以推論出
  1. 你不能改變 p1 與 p2
  2. 你必須返回他們相加的結果
這會導致一個問題
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 = 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
Arr operator+(Arr lhs, Arr const &rhs){
    cout << "lhs+rhs : ";
    return lhs += rhs;
}
兩個是等價的
上面的是把copy寫在Return的時候,那時候才創建一個新類別並複製。
下面的是傳入的時候就發生copy,那時候就創建一個新類別了。
我建議你用下面的版本,因為那是官方給的XDD
能想到的差別是,如果你的op+不是常規的方式,有自訂什麼特別功能,不像文中的功能只是呼叫op+=()就完事了,在定義內有複雜的運算,這時候上面的寫法,lhs這個變數就不能動他,因為傳進來的時候帶const,這會造成很大困擾的歐!
如果是常規狀態,如文內範例,就是等價的沒什麼差~
內文就不修改了,用補充的方式添加,修改還要校正QQ
沒校正怕哪邊失誤多一個字少一個字之類的出錯~~

參考:

  1. [心得] operator+ 與 operator+= 的設計
  2. 使用 friend 函式重載運算子

2017年2月21日 星期二

如何讓 Windows10 8 預設為英文輸入法 [或CMD預設為英文]

如何讓 Windows10 8 預設為英文輸入法 [或CMD預設為英文]


這其實是預設輸入法的問題而已Win10預設是中文輸入法其實很不方便
更改兩項設定即可讓系統預設都是英文輸入法了。

讓新注音輸入法 - 預設為英文

對著你右下角的輸入法(英或中的圖示)按右鍵
選擇 內容 將預設輸入模式改為英數模式
但這僅可以保證重新開機時一開始是英文輸入法而已,無法避免在別人的程序切為中文後切回來之後也變成中文了。
想像一個場景你現在開著終端機用英文輸入指令,突然想不起某個指令怎麼用,於是打開chrome切到中文輸入法打中文搜尋,這時候你切回來終端機的時候就變回英文了。
要解決這個問題就是要把輸入法分開,不要讓他用單一個狀態表示全部程序。

分開輸入法

Win10 改版之後設定變了這邊保留舊版與新板說明

Win10 1803

搜索地區及語言
然後進入進階鍵盤設定
打勾使用不同輸入法

Win10 1803 以前(不包含)

搜索地區及語言
開啟後進入
變更輸入法
進階設定
讓我為每個應用程式設定不同輸入法


完結

接下來開啟任何程式都可以保證是英文輸入法了

C++ 重載下標符號 const 與非 const 寫兩次整合成一次的辦法

重載operator[]符號

tags: C++ Concept
解決必須重複打兩次的問題

重載下標[]符號

如果一個類別的屬性是帶 const 屬性,那麼他將會呼叫 const 版本的
const Arr a(5);
const int & operator[](size_t idx) const{...}
即便不使用到帶const屬性的類別宣告
仍然有可能在重載運算符號時用上(待確認印象中記得會)
Arr a(5), b(5);
a=a+b;
你就必須寫兩份重載函式
const int & operator[](size_t idx) const{...}
int & operator[](size_t idx){...}
除非確定了,不然還是乖乖補上兩種代碼可能比較省事。
這時候就產生一個問題,代碼可能需要寫兩次,至直接導致修改時要改兩處,麻煩了不少。

參考代碼

/*****************************************************************
Name : 重載下標符號 const 函式
Date : 2017/02/20
By   : CharlotteHonG
Final: 2017/02/20
*****************************************************************/
#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

class Arr{
public:
    // 建構子
    Arr(int len): arr(len){
        iota(arr.begin(),arr.end(),1);
    }
    // 重載下標符號
    int & operator[](size_t idx){
        cout << "**Non_";
        return const_cast<int&>(static_cast<const Arr&>(*this)[idx]);
    }
    const int & operator[](size_t idx) const{
        cout << "**Const**" << endl;
        return arr[idx];
    }
private:
    vector<int> arr;
};

int main(int argc, char const *argv[]){
    // **Const**
    const Arr ca(5);
    cout << ca[0] << endl;
    // **Non-Const**
    Arr a(5);
    cout << a[0] << endl;
    return 0;
}

原理

讓 非const版本 的強制去呼叫 const版本的
static_cast<const class&>(*this)
然後再強制解除 const 屬性
const_cast<typename&>
就可以整合成一份主代碼了

是否可以反過來

實際上也是可以把主代碼寫在非const上,語法的運行是可以的,但這會有一種問題,由const呼叫非const貌似有些奇怪:
都已經限制不能修改了,你還去呼叫可以修改的?
也可以換一個角度想,在強制呼叫非const版本的時候,的這個瞬間所返回的參考所參考之處是可以被修改的,const版本的函式居然會有可能被修改,這不符合const的定義。
為避免這不必要的疑慮,應該由非const版本呼叫const版本,避開這些疑慮
/*****************************************************************
Name : 重載下標符號 const 函式
Date : 2017/02/20
By   : CharlotteHonG
Final: 2017/03/17
*****************************************************************/
#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

class Arr{
public:
    // 建構子
    Arr(int len): arr(len){
        iota(arr.begin(),arr.end(),1);
    }
    // 重載下標符號
    int & operator[](size_t idx){
        cout << "Non";
        // return const_cast<int&>(static_cast<const Arr&>(*this)[idx]);
        return arr[idx];
    }
    const int & operator[](size_t idx) const{
        cout << "Const";
        // return arr[idx];
        return static_cast<const int&>(const_cast<Arr&>(*this)[idx]);
    }
private:
    vector<int> arr;
};

int main(int argc, char const *argv[]){
    // **Const**
    const Arr ca(5);
    cout << " = " << ca[0] << endl;
    // **Non-Const**
    Arr a(5);
    cout << " = " << a[0] << endl;
    return 0;
}

參考

ilikekotomi:參考Scott Meyers的Effective C++ 3rd的第三條款

2017年2月9日 星期四

台灣 無法開啟大陸 微博站 [不用VPN翻牆]

台灣 無法開啟大陸 微博站 [不用VPN翻牆]

設定上還挺奇怪的,我發現只有是登入狀態
才能夠看見微博的網站,否則會直接出現404
可是看不見網站怎麼登入呢,連輸入密碼的地方都看不見

解決辦法1

發現404就404繼續等或多開啟次有可能會好
2017.10.21 這一招好像失效了 方法2還有效

解決辦法2

先登入台灣站

台灣微博:http://tw.weibo.com/
這樣瀏覽器就會記住目前的登入狀態,而這個跟大陸站是共通的

然後再登入大陸微博站

因為這時候還是看不見網頁狀態,所以必須手動輸入這個網址來登入
登陸網址:http://weibo.com/login.php
利用台灣站的登錄紀錄,然後再輸入大陸站的登入網址就可以成功登入了。

完成

這時候就可以進入微博查看任何公開貼文了
微博主站:http://weibo.com

解決辦法3 - 手機、平板的網站不受限

直接用以下網址觀看也可以

2017年2月6日 星期一

PCIE x1 的音效卡可以插在 PCIE x16 的插槽上嗎

PCIE x1 的音效卡可以插在 PCIE x16 的插槽上嗎

可以,但是儘量是不要

共用的 PCIE x16

一般來說板子上有兩個以上的 PCIE x16 是為了雙顯示卡去做的,其中兩個是綁在一起的,就是你同時接兩張會以 PCIE x8 兩個去跑,不要接到這個插槽去了。
詳細可以到官網查主機板說明書裡面有會寫哪個是共用的
一般來說如果有三個,那個最上面兩個會是共用的,第三個則是分開的。

異常的電流聲

最初的時候我是把它接再第三個曹為,是可以正常使用的,不過有些問題。主機殼前面板的音效孔不能夠使用,有非常大的電流聲,即便是後主機板仍有明顯的電流聲。
接到 PCIE x1 之後就正常了

2017年2月5日 星期日

電腦主機該放右邊還是左邊

電腦主機該放右邊還是左邊

看起來不起眼的問題,我其實已經想了一段時間,差不多有個普了
看機殼…
對最主要應該還是還是要看機殼設計怎樣,有些真的是刻意做來放左邊的。仔細觀察可以發現大多數的開機扭如果有分左右的,都是在左邊居多的。次多的是置中的按鈕,或是置頂的機殼設計(至頂的兩顆一樣大的也是放左邊為多數)。
開機扭在左邊,應該把主機放置在右邊會比較好使。

通常習慣

大多數的主機應該更適合放右邊
原因很簡單,仔細推從以下原因推敲;看環境來決定,這裡就把它界定成標準辦公室環境吧
  • 維修
  • 工作空間

維修

大多數的機殼都是做成左邊開殼的,也就是左邊打開就可以拆裡面零件了,這樣的方式如果擺放在右邊,維修時就不用特地在移動出來。
不過一般人可能也不會去拆就是了XDD
此外有不少25CM超大側板風扇的機殼,如果放左邊吸封口會被擋住的。

工作空間

大多數人的的習慣都是右手為主,也就是說,會更習慣的空出右邊的空間比較好操作,比如說你要用電腦又要寫字,紙只能選擇放右邊放左邊,很明顯放左邊不是個好選擇,右手寫不到。
於是空出右邊的桌面空間應該更利於操作的,人的位置靠左邊坐。這時候主機如果放左邊腳會卡到。