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;
}
/*==============================================================*/
參考:
  1. [心得] operator+ 與 operator+= 的設計
  2. 使用 friend 函式重載運算子

2017年2月21日 星期二

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

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


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

預設為英文

對著你右下角的輸入法(英或中的圖示)按右鍵
選擇 內容 將預設輸入模式改為英數模式
但這僅可以保證重新開機時一開始是英文輸入法而已

分開輸入法

預設輸入法會繼承,也就是目前為中文開啟其他程式也一定是中文
我們要取消這種繼承關係,讓他可以分別在A程式是英文B程式是中文
搜索地區及語言
開啟後進入
變更輸入法
進階設定
讓我為每個應用程式設定不同輸入法
接下來開啟任何程式都可以保證是英文輸入法了

2017年2月20日 星期一

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月8日 星期三

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

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

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

解決辦法1

發現404就404繼續等或多開啟次有可能會好

解決辦法2

先登入台灣站

台灣微博:http://tw.weibo.com/

在登入大陸微博站

因為這時候還是看不見網頁狀態,所以必須手動登入到這個網址來
登陸網址:http://weibo.com/login.php
利用台灣站的登錄紀錄摸黑登入微博站

解鎖

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

手機、平板站不受限

或者直接用以下網址觀看吧~
應該也可以用這個進入登入狀態

2017年2月6日 星期一

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

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

可以,但是儘量是不要

共用的 PCIE x16

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

異常的電流聲

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

2017年2月4日 星期六

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

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

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

通常習慣

大多數的主機應該更適合放右邊
原因很簡單,仔細推從以下原因推敲;然後還有一點很重要,環境界定,這裡就把它界定成標準辦公室環境吧,畢竟應該是最多的。
  • 維修
  • 工作空間

維修

仔細觀察可以發現幾乎所以機殼都是做成左開的,也就是左邊打開就可以拆裡面零件了,這樣的方式如果擺放在右邊,維修時就不用特地在移動出來。
此外有不少25CM超大側板風扇的機殼,如果放左邊吸封口會被擋住的。
少數也有機殼開右邊就是了

工作空間

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