2017年5月28日 星期日

C++ ios::app 與 ios::ate 打開方式有什麼不同

C++ ios::app 與 ios::ate 打開方式有什麼不同

用一句話形容:
兩個都把檔案指針移動到結尾,差別在於能否有權限修改、讀取原本的檔案
  • 檔案指針就像開啟記事本或聊天室窗打字時的 | 的意思。
假設有一個記事本 f.txt 內容是
ABCED

ios::ate

at end 開啟檔案並將檔案指針移動到結尾,稍後你還可以移動指針到前方修改或讀取。
fstream f("f.txt", ios::in|ios::out|ios::ate);
cout << f.tellg() << endl;
獲得目前指針位置在5
更動指針位置到起點開始算第0個位置
f.seekg(0, f.beg);

ios::app

append 原意為加上的意思,意味著沒有權限更動原本的存在物。
開啟檔案並將檔案指針移動到結尾,稍後並沒有權限可以讀取或修改前方。
fstream f("f.txt", ios::in|ios::out|ios::app);
cout << f.tellg() << endl;
回傳指針位置 0 把結尾直接當作起點了。

小提示

開檔的時候上面的 ios::in|ios::out 不可以省略,必須詳細指明要寫、要讀還是都要。
不可以像這樣寫
fstream f("f.txt", ios::app);
沒有表明到底是要怎麼樣。

2017年5月26日 星期五

CodeBlocks 掛載 MinGW 更新最新版本編譯器

CodeBlocks 掛載 MinGW 更新最新版本編譯器




安裝 CodeBlocks 之後並不能馬上使用需要一些修改,如何與編譯器連結。
最新版本的 gcc 可以從這裡下載
下載完之後安裝在C曹預設位置即可。


掛載編譯器

除非你是裝舊版的MinGW,不然如果都安裝好了什麼都不做直接運行會跳出這樣的錯誤。
The compiler's setup (GNU GCC Compiler) is invalid, so Code::Blocks cannot find/run the compiler.
Probably the toolchain path within the compiler options is not setup correctly?! (Do you have a compiler installed?)
Goto "Settings->Compiler...->Global compiler settings->GNU GCC Compiler->Toolchain executables" and fix the compiler's setup.
Skipping...
Nothing to be done (all items are up-to-date).
先開啟設定
改一下位置,找到你的bin資料夾
把下面三個修改成這樣
不過其實就是按照 bin 裡面的檔名修改,如果之後改版有改檔名來這裡找
再來還有一個地方要修改
找到這個檔案
再來就可以按F9編譯了

C and C++ 字串(string) 轉 數字,數字 轉 字串(string) 綜合整理

C and C++ 字串(string) 轉 數字,數字 轉 字串(string) 綜合整理

綜合了一下常見的方法,可以把
整數(int)、浮點數(float)、長整數(long long)
字串(char*)(string) 互轉
/*****************************************************************
Name : C and C++ 字串轉整數,整數轉字串 綜合整理
Date : 2017/05/26
By   : CharlotteHonG
Final: 2017/05/26
*****************************************************************/
#include <iostream>
#include <sstream>
#include <cstring>
#include <string>
using namespace std;

constexpr unsigned long long num = 3141592653589793238;
constexpr int num2 = 314159265;
const char* str = "3141592653589793238";
const char* str2 = "314159265";

void to_str1(){
    char str[20];
    strcpy(str, to_string(num).c_str());
    cout << "to_str1 = " << str << endl;
}
void to_str2(){
    char str[20];
    itoa(num2, str, 10);
    cout << "to_str2 = " << str << endl;
}
void  to_str3() {
    char str[20];
    sprintf(str,"%d",num2);
    cout << "to_str3 = " << str << endl;
}
void to_num1(){
    unsigned long long num = stoull(str);
    cout << "to_num1 = " << num << endl;
}
void to_num2(){
    stringstream ss(str);
    unsigned long long num;
    ss >> num;
    cout << "to_num2 = " << num << endl;
}
void to_num3(){
    unsigned long long num = strtoull(str, NULL, 10);
    cout << "to_num3 = " << num << endl;
}
void to_num4(){
    long long num = atoll(str);
    cout << "to_num4 = " << num << endl;
}
void to_num5() {
    unsigned int num;
    sscanf(str2,"%d",&num);
    cout << "to_num5 = " << num << endl;
}

int main() {
    to_str1();
    to_str2();
    to_str3();
    to_num1();
    to_num2();
    to_num3();
    to_num4();
    to_num5();
    return 0;
}

static function or static member 用途整合整理

static function or static member 用途整合整理

非類別

static variable

有兩種用途,一種是在 function() 內的靜態變數,一種是全域的靜態變數

全域內的靜態變數

可以有效的隔絕變數被其他檔案使用,如果你有多份檔案
  • a.cpp
  • b.cpp
那麼被設置為 static 的全域變數只有那一份檔案可以使用,如果沒有設置為 static 那麼其他檔案可以透過 extend 來使用、取得。

函式內的靜態變數

該函式的生命週期會延遲到整個程式結束
函式內的變數在函式呼叫結束的時候會清除所有變數,妳果你希望某個函式可以計數,總共被呼叫幾次,可以宣告一個靜態變數,每次都加1。
void fun(){
    static itn i;
    ++i;
}

靜態的函式

保護函式不會被其他cpp檔案存取
與全域變數差不多,禁止其他檔案存取這個函式
static void fun(){;
}
如此一來只有該份 .cpp 檔案可以存取使用這個函式

類別

類別內的靜態變數成員

不管宣告幾個物件,他還是同一個,可以連結所有物件
簡單來說創建了兩個物件 a 與 b ,現在由 a 把他設定為 10 , b 也可以看到是 10。
一樣可用在計數總共被創建幾次。
設定方式比較特別宣告後只能在類別外初始化
class OBJ{
    static int c;
};

int OBJ::c = 0;

類別內的靜態函式成員

這個更特別一些,可以把他解釋為
只為特定class工作,並且有特權的普通函式。
特權指的是可以存取私有成員,普通函式指的是沒有帶 this 指針。
可能會好奇沒有this指針怎麼存取,這就像 friend 函式一樣,你把物件指針導入就好了。
如果你想弄一個可以產生特定有效數據的類別,比如說 RGB
enmu RGB{R, G, B};
你想藉此避免使用者輸入無效數據
void fun(RGB img){

}
這麼一來就安全了,不過其實還有一種問題
某個人忘記初始化他..
RGB img;
手動建一個安全的函式群
class RGB{
public:
    static RGB R(){return RGB(0);}
    static RGB G(){return RGB(1);}
    static RGB B(){return RGB(2);}
private:
    RGB(int num): rgb(num){}
    unsigned rgb;
};
void fun(RGB img){}
好了,現在再也沒有人有能力建造出除了 0, 1, 2 以外的 RGB 了,因為RGB的建構子是私有的。現在只有 static 有特權可以存取私有的建構子。
fun(RGB::R());
你現在可以大坦的不檢查輸入的有效性了,因為他絕對安全又有效。


C 語言仿類別

根據以上的特性不知道你有沒有發現到,這就好像一個物件!
是的在C語言裡面把一份 .c 檔案視為一個物件,替變數或函式加上 static 就好像替他們宣告為 private 一樣。
雖然活運性沒有真實物件那麼好用,不過至少可以把變數與函式切的乾乾淨淨的,一塊一塊的,閱讀性與安全提升很多。
安全指的是多人合作時某人用你的函式,而妳不知道,事後你稍微調整個函式造成程式錯誤,你會很難找出來;以及某人 extend 你的變數,也是會發生一樣的事情。(雖然本來應該良好的溝通避免這種事情,不過從根本禁止還是方便有效些)。
不過別人跑來改掉妳的static就~XDD

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

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

函式名稱的衝突

繼承重要的用途之一就是
利用父類別的指針型別來建立子類別
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);
你將得到錯誤的結果