2017年5月26日 星期五

多重繼承時函式名稱一樣怎麼重載虛擬函式

多重繼承時函式名稱一樣怎麼重載虛擬函式

函式名稱的衝突

繼承重要的用途之一就是
利用父類別的指針型別來建立子類別
Fa* p = new SonA;
p->fun();
如此一來父類別可以提供預設的行為,子類別也可以視需求更改行為或增加資料成員。
宣告都用父類別用途意旨fun()可以接收所有子類,這讓程式設計得到很大彈性。
但是在多重繼承的時候如果兩個不同的父類別名稱一樣會造成”曖昧”衝突,並且子類別沒辦法重新指定函式行為。
// Base class
class A1 {
public:
    virtual void draw(){cout << "A1" << endl;}
};
class A2{
public:
    virtual void draw(){cout << "A2" << endl;}
};
現在同時繼承他們
class A: public A1, public A2 {
public:
    void draw(){cout << "A draw" << endl;}
};
你的 draw() 到底是重寫 A1::draw() 還是 A2::draw()
沒有方法區別


新增中介轉發類別

我們可以繞個彎使用一對class重新導向函式名稱
class AuxA1: public A1{
public:
    virtual void A1draw() = 0;
    virtual inline void draw(){A1draw();}
};
class AuxA2: public A2{
public:
    virtual void A2draw() = 0;
    virtual inline void draw(){A2draw();}
};
現在,你繼承 AuxA1 重寫新的 A1draw() 就等於重載 A1 的 draw()
// 重載個別的 draw()
class A: public AuxA1, public AuxA2 {
public:
    void A1draw(){cout << "Override A1 draw" << endl;}
    void A2draw(){cout << "Override A2 draw" << endl;}
};
你可以放心的使用他們
A1* a1 = new A;
A2* a2 = new A;
a1 -> draw();
a2 -> draw();


虛擬函式的彈性

另外重申一下虛擬繼承的好處,像這樣的 funtion

class B{};
class D1: public B{};
class D2: public B{};

void fun(B* p){
    p->fun();
}
你可以根據輸入的指針不一樣選擇不一樣的行為
B* b1 = nullptr

b1 = new D1;
fun(b1); // 呼叫 D1 fun
b1 = new D2;
fun(b2); // 呼叫 D2 fun


範例代碼

/*****************************************************************
Name : 多重繼承時函式名稱一樣怎麼重載虛擬函式
Date : 2017/05/21
By   : CharlotteHonG
Final: 2017/05/26
*****************************************************************/
#include <iostream>
using namespace std;

// Base class
class A1 {
public:
    virtual void draw(){cout << "Base A1 draw" << endl;}
};
class A2{
public:
    virtual void draw(){cout << "Base A2 draw" << endl;}
};
//----------------------------------------------------------------
// 沒辦法重載個別的 draw()
// class A: public A1, public A2 {
// public:
//     void draw(){cout << "A draw" << endl;}
// };
//----------------------------------------------------------------
// 轉發 (明白的指定轉發函式)
class AuxA1: public A1{
public:
    virtual void A1draw() = 0;
    virtual inline void draw(){A1draw();}
};
class AuxA2: public A2{
public:
    virtual void A2draw() = 0;
    virtual inline void draw(){A2draw();}
};
// 重載個別的 draw()
class A: public AuxA1, public AuxA2 {
public:
    void A1draw(){cout << "Override A1 draw" << endl;}
    void A2draw(){cout << "Override A2 draw" << endl;}
};
//----------------------------------------------------------------
int main() {
    // 繼承的使用方式
    A1* a1 = new A;
    A2* a2 = new A;
    a1 -> draw();
    a2 -> draw();
    // 這樣仍然會因為曖昧不能使用
    // A a;
    // a.draw();
    return 0;
}
//----------------------------------------------------------------

2017年5月23日 星期二

Visual studio 如何更換C++標準 使用 C++11 或 C++14

Visual studio 如何更換C++標準 使用 C++11 或 C++14

這個要在個別的專案設定,每開一個新專案就重新設定一次
然後在這裡
或者直接安裝新版的2017,預設就是C++14了

2017年5月22日 星期一

float double 為什麼 指針 陣列 轉型出來數值會異常 (錯誤)

float double 為什麼 指針 陣列 轉型出來數值會異常

主要是因為浮點數儲存格式比較特別,直接轉成int通常會得到錯誤的結果
看看下面的例子
float f=1, *fp=&f;
int* i = (int*)fp;
cout << "i=" << *i << endl;
出來的結果為
i=1065353216;
會是一個很奇怪的數字,主因是因為不正確的解析
這裡說一個我自己的比喻,可能不是很正確,但可以幫助理解
指標轉型 (int*) 選擇用什麼方式解密 (還沒解密)
選擇後 reference 才是真正的解密
如文中一開始的情況
嘗試把一個 float 加密的資料 fp
float f=1, *fp=&f;
選擇使用 int 解密方式
int* i = (int*)fp;
解密得到錯誤的結果
cout << "i=" << *i << endl;
如何獲得正確的結果需要選擇使用正確的方式解密
cout << "i=" << *(float*)fp << endl;
轉譯的過程需要一個實體空間的暫存
int temp = *(float*)fp;
    i = &temp;
int 與 float 儲存格式是不一樣的,如果你想使用 int 的解密方式
(某個函式的接收參數)
void fun(int*);
你只能重新劃分一塊新的鎮列儲存,否則你不能期待把一個A方法加密的檔案檔用B方法解密
void fun(int*);

float* fp;
fun((int*)fp);
你將得到錯誤的結果

2017年5月20日 星期六

C and C++ 如何用簡易的方式提取 特定位元(bit) 又可直接存取整個數據

C and C++ 如何用簡易的方式提取 特定位元(bit) 又可直接存取整個數據

這裡用了幾個技巧來達成這個功能
  • Union
  • struct 位元切割
  • 位元運算

Union

用一句話形容
可以自由的切換檢視角度
這是我自己創的詞可能不是很正確,不過可以很容易的理解
在記憶體內全部都是0和1,並不會區別什麼是 int 什麼是 char 那只不過是經過編譯器的解析,或的正確的數值。
檢視角度指的是對記憶體資料的型態解析,比如說以下的例子
int i=65;
void* p=&i;
現在 P 指向某個記憶體位置,但是不知道他是什麼型態
現在詳細指明他是什麼型態 (先不管成功與否的安全問題)
char* str = (char*)p;
現在你告訴編譯器,當我找str地址的使用char的型態來檢視
cout << str << endl;
現在你可以正確的顯示 ‘A’
這個過程需要指名轉型,使用Union可以共用記憶體
共用之後需要什麼型態就直接用什麼型態
自由的切換檢視角度
union S{
    int i;
    char c;
};
現在我們讓 int 與 char 共用一塊記憶體
    S a;
    a.i = 65;
    cout << "a.c = " << a.c << endl;
結果顯示為 A,可以省去繁瑣的轉型,需要什麼在補上什麼
另外~浮點數類的如 float doubel 算法不一樣直接轉數值會不是你想要的,不能這樣用


Struct 位元切割

Struct與Union配合可以任意的取出任意位元
using uch = unsigned char;
union Bit{
    struct Bit_dev{
        uch data: 4;
        uch: 2;
        uch data2: 2;
    } bit;
    uch raw;
};
};
這裡特別的寫法可以獲取特定的位元,不要的部分可以不用設變數
養成好習慣記得把struct的大小分配補滿,比較不會產生誤會
如果你只要取中間的話

using uch = unsigned char;
union Bit{
    struct Bit_dev{
        uch:3;
        uch data:4;
        uch:1; // 可以省略不過補上比較好
    } bit;
    uch raw;
};
用法如下
/*****************************************************************
Name :
Date : 2017/05/07
By   : CharlotteHonG
Final: 2017/05/07
*****************************************************************/
#include <iostream>
#include <bitset>

using uch = unsigned char;
union Bit{
    struct Bit_dev{
        uch data: 4;
        uch: 2;
        uch data2: 2;
    } bit;
    uch raw;
};

int main(int argc, char const *argv[]) {
    S a;
    a.i = 65;
    cout << "a.c = " << a.c << endl;

    Bit b;
    b.raw = 0xF7;
    cout << "b.bit.data=" << (bitset<8>)b.raw << endl;
    cout << "b.bit.data=" << (int)b.bit.data << endl;
    cout << "b.bit.data=" << (int)b.bit.data2 << endl;

    return 0;
}
結果
a.c = A
b.bit.data=11110111
b.bit.data=7
b.bit.data=3
需要注意的部分是順序從左開始還是從右開始,自己看一下很容易可以看出


位元運算

這個比較沒有難度,不過可能不好理解,邏輯運算 &
unsigned char bit = 0xCF;
cout << "bit=" << (bitset<8>)bit << endl;
會印出 11001111 目標是取出中間4個 0011 也就是 3
先右移兩位把不要的推掉
bit >>= 2;
cout << "bit=" << (bitset<8>)bit << endl; // bit=00110011
然後讓右邊4個做 & 運算 (左邊的沒寫到的會被捨去)
bit &= 0xF; // 1111
cout << "bit=" << (bitset<8>)bit << endl; // bit=00000011
如果要取3個的話就不要最左邊的
bit &= 0x7; // 0111

android sony 手機的 skype 無法使用表情圖案,出現鍵盤

android sony 手機的 skype 無法使用表情圖案,出現鍵盤

很久之前遇過一次最近又出現了一次,症狀就是skype怎麼樣都無法按出表情,按下表情欄位會變成打字鍵盤。
主要是某些可以重製繪圖的背景軟體造成的,之前是因為裝了 easy tough ,手機桌面會有一顆iphone浮球,導致出錯。
後來將那個軟體移除即可
最近的一次的是因為 ES File 檔案管理員更新了!新增了一個浮球的功能導致出問題的,不過我已經有嘗試去軟體設定內關閉浮球還是一樣。
貌似有兩個浮球,一個是縮小到桌面出現的,一個是ES File軟體內的,兩個都要關閉的樣子,我那時候只有關一個。
解決辦法我是重新裝,然後第一次開啟的引導介面可以選擇不要開,直接在那裏選擇不開。
後來推測或許兩個都關掉應該就不用重裝了。

2017年5月5日 星期五

如何降低編譯的依存關係

如何降低編譯的依存關係

tags: C++ Concept2

依存關係

依存關係指的是A class的實作必須在寫在B class之前,他們之間存在相依關係,A一定要寫在B之前;或A檔案一定要先被編譯,他們之間不能夠單純依靠宣告來通過編譯。
以下降低解說難易度將檔案縮減至一個單一檔案敘述
假如有一個自訂類別為 Name
// Name class 前置性宣告
class Name;
// Name class 定義 (不是成員函式定義)
class Name{
public:
    Name(string n);
    friend ostream& operator<<(ostream& out, Name const & n);
private:
    string n;
};
那麼要使用這個型別的時候,該class定義必須出現在使用之前
// Name class 宣告
// Name class 定義
Name a("chg");
如果你把class定義移動到下方則會出問題
(Name的函式定義可以移動到下方)
// Name class 宣告
Name a("chg");
// Name class 定義
則會出現相依的問題,產生Name的時候找不到定義;其他class如果要使用Name這個class,會造成Name一定要擺在那之上才可以編譯,如果要交錯互相使用麻煩就大了,誰在上都不對。


Handle Class

解決辦法,可以使用指針來占個位置不把它實作,這樣就不會需要他的實作了
只需要前置性宣告。
// Name class 宣告
Name* a;
// Name class 定義
應用在剛剛說的問題上代碼大概就長這個樣子
// Tw class定義
class Name;
class Tw{
public:
    Tw(string name);
private:
    Name* n;
};
// Name class定義
class Name{
public:
    Name(string n);
    friend ostream& operator<<(ostream& out, Name const & n);
private:
    string n;
};

// Tw建構子
Tw::Tw(string name): n(new Name(name)){
    cout << "n=" << *n << endl;
}
// Name 函式定義
Name::Name(string n): n(n){}
ostream& operator<<(ostream& out, Name const & n){
    out << n.n;
    return out;
}
如此一來順序的相依性就變得兩者皆可(函式的定義也是)
此外這裡的指針也可以使用沒有初始化的vector取代
vector<Name> n;
不要初始化內容
vector<Name> n("name");
也不要初始化長度
vector<Name> n(1);
反例中的代碼因為Name沒有預設建構子(引數可為無的建構子),僅能使用 push_back() 初始化
// Tw建構子
Tw::Tw(string name){
    n.push_back(name);
    cout << "n=" << n[0] << endl;
}


Protocol Class

利用虛擬函式的特性,不需要先做,藉此避開宣告時必須要要定義的狀況。
// Name class 宣告
// 純虛擬類別定義(抽象類別宣告內含Name)

// Name class 定義
// 繼承抽象類別


參考代碼

Handle Class
/*****************************************************************
Name : Handle Class
Date : 2017/05/04
By   : CharlotteHonG
Final: 2017/05/04
*****************************************************************/
#include <iostream>
#include <string>
#include <vector>
using namespace std;

// Tw class定義
class Name;
class Tw{
public:
    Tw(string name);
private:
    Name* n;
    // vector<Name> n;
};

//================================================================
int main(int argc, char const *argv[]){
    Tw a("chg");
    return 0;
}
//================================================================

// Name class定義
class Name{
public:
    Name(string n);
    friend ostream& operator<<(ostream& out, Name const & n);
private:
    string n;
};
// Tw建構子
Tw::Tw(string name){
    n = new Name(name);   // point
    // n.push_back(name); // vector
    cout << "n=" << n[0] << endl;
}
// Name 函式定義
Name::Name(string n): n(n){}
ostream& operator<<(ostream& out, Name const & n){
    out << n.n;
    return out;
}
Protocol Class
/*****************************************************************
Name :
Date : 2017/05/04
By   : CharlotteHonG
Final: 2017/05/05
*****************************************************************/
#include <iostream>
using namespace std;
// Tw class定義
class Name;
class Tw{
public:
    virtual ~Tw(){}
    virtual string name()=0;
    static Tw* makeTw(Name const & name);
};

// Name class定義
class Name{
public:
    Name(string const & n);
    operator string&(){
        return n;
    }
    friend ostream& operator<<(ostream& out, Name const & n);
private:
    string n;
};
//================================================================
int main(int argc, char const *argv[]){
    Tw* p;
    Name chg = string("chg");
    p = Tw::makeTw(chg);
    cout << p->name() << endl;
    return 0;
}
//================================================================

// Tw class定義
class RealTw: public Tw{
public:
    RealTw(Name const & name): n(name){}
    string name(){return n;}
private:
    Name n;
};

// makeTw 函式定義
Tw* Tw::makeTw(Name const & name){
    return new RealTw(name);
}

// Name 函式定義
Name::Name(string const & n): n(n){}
ostream& operator<<(ostream& out, Name const & n){
    out << n.n;
    return out;
}