顯示具有 筆記 標籤的文章。 顯示所有文章
顯示具有 筆記 標籤的文章。 顯示所有文章

2015年3月19日 星期四

用 cmd line 執行 MFC 後的訊息顯示

一個卡了我不少時間的問題

最後終於找到一個可以解決的辦法,所以記錄一下

//----------------------------------------------------------------------

需求是有一個 MFC 的程式,並經由 cmd line 呼叫
而且在程式執行失敗時,可以顯示訊息到 cmd 當下的視窗中

PS. 接收命令參數使用下面的方法
CString strCommandLine(GetCommandLine());
CommandLineToArgvW(CString(GetCommandLine()), &nArgCount);

處理過程中所遇到的問題
1. 無法在當下的 cmd 中 printf 訊息
2. printf 出來的訊息位置不佳

第一點比較快解決,有兩種方法,我是選用第一個,較簡單
第一種:
AttachConsole(ATTACH_PARENT_PROCESS); //直接取得當下 process
_cprintf("程式執行失敗");

第二種方法是下面這個網址的做法
http://blog.csdn.net/panchunrong/article/details/8930603


第二個問題就真的卡很久了
先說明一下狀況
----------------------------------------------------------------------
C:\>MFC.exe

程式執行失敗            <-目標顯示狀況:在下面的 C:\> 之前顯示

C:\>
----------------------------------------------------------------------
C:\>MFC.exe

C:\>程式執行失敗        <-實際顯示狀況:在 C:\> 之後才顯示
----------------------------------------------------------------------

在思考時主要有兩個方向

  • 進程
  • Console 緩衝區字元處理

進程的思考就是要在 C:\> 出現之前就將訊息印出
不過似乎不是能夠輕易解決
所以最後是用「Console 緩衝區字元處理」解掉的
再配合一些小技巧 (e.g. 實現 "Enter" 輸入,清空一行緩衝區)
就能完美的解決問題

程式碼如下:
AttachConsole(ATTACH_PARENT_PROCESS); //綁定當下cmd窗口
HANDLE hHandle = GetStdHandle(STD_OUTPUT_HANDLE); //綁定 handle
CONSOLE_SCREEN_BUFFER_INFO    sbi; //窗口緩衝區信息
GetConsoleScreenBufferInfo(hHandle, &sbi); //讀取console內容
char *szLine;  /* buffer to hold the line read from the console */
szLine = (char *) malloc(sbi.dwSize.X);
COORD coordLine = {0, 0}; /* coordinates of where to read characters from */
DWORD dwCharsRead(0);
SHORT StartLine(0);
for(int i=sbi.dwCursorPosition.Y ; true ; i--)
{  
 coordLine.X = 0;
 coordLine.Y = i;
 //
 ReadConsoleOutputCharacterA(hHandle, szLine, sbi.dwSize.X, coordLine, &dwCharsRead); //讀取console緩衝區的一行內容
 int j = sbi.dwSize.X - 1;
 szLine[j--] = 0; /* null terminate */
 while (szLine[j] == ' ')
 {
  szLine[j--] = 0;
 }
 
 if (j==-1) //代表當下的行無任何字元
 {
  StartLine = i;
  break;
 }
}
 
for (int i=StartLine ; i<=sbi.dwCursorPosition.Y ; i++) //清空每一行(避免命令列路徑大於一行時)
{
 coordLine.Y = i;
 FillConsoleOutputCharacter(hHandle, _T(' '), sbi.dwSize.X, coordLine, &dwCharsRead);
}

coordLine.Y = StartLine+1;
SetConsoleCursorPosition(hHandle, coordLine);  //移動Console游標位置
_cwprintf(L"程式執行失敗\n");
keybd_event(VK_RETURN, 0 , 0, 0); //實現按下 "Enter" 鍵

2013年10月12日 星期六

Visual Studio 2008 + Visual Assist X + Qt

1. Visual Studio 2008 (English)

不斷的下一步…就安裝完成囉~



2. Visual Assist X

版本:1949

官網:http://www.wholetomato.com/default.asp

付費軟體,官網有試用版可以用

安裝完後可以在 Visual Studio 2008 工具列看到 Visual Assist X 的項目,就表示安裝成功囉~


3. Qt

下載 Qt4.8.4 + VS add-in 1.1.11

qt-win-opensource-4.8.4-vs2008.exe
http://download.qt-project.org/archive/qt/4.8/4.8.4/

qt-vs-addin-1.1.11-opensource.exe
http://download.qt-project.org/official_releases/vsaddin/

依次安裝後(順序好像不影響),成功的話就一樣會看到工具列上出現 Qt 的項目囉~


PS. 我在用 Qt5.1 + addin 1.2.2 時,新建 Qt project 時會出現 unable to find qt build


Google 後發現 Visual Studio 的 Qt Option 裡面竟然沒 include 到 Qt5.1


add Qt5.1 的路徑給它,它竟然說路徑下的 qmake.exe could not be found,可是這個檔案明明就在,也沒有中文路徑的問題,於是就改回用 4.8.4 了 (沒骨氣…)

有正確 include 到會是長這樣子:


Visual Assist X 顏色配置

配色的部分每個人的習慣不同,所以就不多說明

只是有幾個狀況我覺得可以分享一下


文字顏色


在選擇文字顏色時有 Palette, Web, System 這個分頁可以選,預設都是在 Palette
     
可是因為我是用黑底白字

所以對於會底色比較不能接受太亮的顏色,不然就會發生反白時字都看不清楚的狀況…

但是若選擇 System 的顏字的話,在出現底色時,字體顏色會變成黑色

這樣就不會有看不清楚的狀況了!! 


搜尋文字底色

Visual Assist X 會把留在 Visual Studio 搜尋框中的字都標上為淡黃色的底色

但是因為我字體是用白字,所以反而會看不清楚

Visual Assist X 又不能直接更改顏色

所以根據它的說明


可以找到下面這個網址:

http://www.wholetomato.com/products/features/highlightFindResults.asp

照這個網路就可以修改顏色囉~

不過這個功能是付費版本才有的…殘念…


顯示 Qt 成員函數

本來 Visual Assist X 就可以找到 Qt 的成員函數


可是之前用到一半時忽然這個功能就失效了…0rz 

Google 後說可以把 Qt 手動 include 到 Visual Assist X 中

 (Platform 應該還要先選為 Custom 才可以新增路徑)

只是這樣好像會把全部的成員函數都 include 進來,而不會只顯示只能使用的成員函數

不過功能恢復比較重要,這點影響倒是不大

But,讓人生充滿挑戰的就是這個 but

在前陣子這個功能又失效了…

目前無解中,只能看它的臉色吃飯了 QQ


參考資料:

[第一节]Qt安装—图文并茂搭建VS2008+QT开发环境
http://www.cnblogs.com/zwq194/archive/2011/02/19/1958713.html

QT Start with Visual Studio 2008
http://cigarhuang.wordpress.com/2011/01/27/hello-world/

QT与Visual Assitst X的集成问题
http://blog.csdn.net/ykm0722/article/details/6958774

添加 第三方库的源代码 到 Visual Assist X
http://blog.csdn.net/yunccll/article/details/7219778

2012年7月12日 星期四

[轉錄]Re: [問題] 物件導向的好處是....

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [轉錄]Re: [問題] 物件導向的好處是....
 時間  2012/07/12 Thu 23:21:47
───────────────────────────────────────

※ 本文轉錄自 [Big-Bear] 看板

發信人: johnhao1206.bbs@ptt.cc (johnhao1206.bbs@ptt.cc) 看板: Big-Bear
標  題: Re: [問題] 物件導向的好處是....
發信站: 2012/07/12 Thu 17:19:50

作者: givemepass () 看板: java
標題: Re: [問題] 物件導向的好處是....
時間: Tue Dec 14 16:57:35 2010

※ 引述《kidd0730 (大阪掛川大不同)》之銘言:
: 我目前對物件的認知是可以延伸出很多類似的類別
: 比如人的物件  可以延伸出男人女人等類別
: 且可以擁有各自的屬性或行為
: 這樣就是物件導向  可以減少相同的code出現
: 如果我的認知沒錯的話
: 那問題來了
: 以前非物件導向的語言 透過method或是function的呼叫
: 不是也可以做到"類似"的功能嗎??
: 還是物件導向有其他更強大的地方呢?
強大的地方就是在多型!!

寫了一個例子 有點長 如果觀念有錯 請大家鞭策一下^^
假設你已經創造一個遊戲

裡面有一個角色是一個劍士

class SwordsMan{
    public String name;
    public SwordsMan(String userName){
        name = userName;
    }
    public void run(){
        System.out.println("跑");
    }
    public void fight(){
        System.out.println("揮舞劍");
    }
}

裡面又有一個角色是魔法師

class Magician {
    public String name;
    public Magician(String userName){
        name = userName;
    }
    public void run(){
        System.out.println("跑");
    }
    public void fight(){
        System.out.println("魔法攻擊");
    }
}

這時候你就會想到 每一個角色當中 都有重複的跑方法
這樣好了 我就寫一個方法 然後讓所有的角色來繼承(inheritance)
於是就寫出了這樣的程式

class Character{
    public String name;
    public Character(String userName){
        name = userName;
    }
    public void run(){
        System.out.println("跑");
    }
    public void fight(){}
}

因為每個角色的攻擊方式都不一樣
然後所有的角色都去推翻fight()這個方法(覆寫)

class SwordsMan extends Character{
    public SwordsMan(String username){
        super(username);
    }
    public void fight(){
        System.out.println("揮舞劍");
    }
}
class Magician extends Character{
    public Magician(String username){
        super(username);
    }
    public void fight(){
        System.out.println("魔法攻擊");
    }
}
..其他角色同樣繼承覆寫...

這時候 只要將魔法師 、劍士或其他角色實體化(instance)
就可以使用各自的方法了

可是這樣會出現一個問題,就是程式靈活度不夠,
想看看今天我有50個角色,裡面有20個角色攻擊都是揮舞劍,
這樣我每個角色都要去覆寫fight()不就累死了,

再說,如果我要修改每一個揮舞劍的角色改成揮舞刀,
那這樣也是會浪費很多時間,
所以我們就必須要把常常會變動的部分取出來,

所以我們將fight()拿出來寫成介面(interface)

interface class FightMethod{
    public void fight();
}

然後我們用SwordsMan這個類別去繼承它嗎?
不!

我們已經繼承Character, Java不允許多重繼承
所以我們實作(implement)它嗎?
不!這樣跟剛剛覆寫fight()方法不就一樣的問題?
複合才是我們要的答案!

我們定義一個使用劍的類別去實做它

class UseSword implements FightMethod{
    public void fight(){
        System.out.println("揮舞劍");
    }
}

然後在劍士的類別改成這樣

class SwordsMan extends Character{
    private UseSword charUseSword;
    public SwordsMan(String username){
        super(username);
        charUseSword = new UseSword();
        charUseSword.fight();
    }
}

這樣寫很有問題! 什麼問題?

回想剛剛的問題,又在這裡發生了,這樣每一個角色都必須宣告一個攻擊的物件,
並且配置實體給它,那100個角色仍然要配置100個實體,
你會累死...

怎麼辦呢?

多型就是用在這個地方
我們這樣宣告

FightMethod fm = new UseSword();
fm.fight();
FightMethod fm是形式型態的宣告,而
new UseSword()才是你實際型態的宣告。


關於多型(Polymorphism),「當某變數的實際型態(actual type)和形式型態
(formal type)不一致時,呼叫此變數的 method,一定會呼叫到「正確」的版本,
也就是實際型態的版本。

所以我們這樣就可以在Character類別直接這樣宣告

class Character{
    public String name;
    protected FightMethod fm;
    public Character(String userName){
       name =  userName;
    }
    public void run(){
        System.out.println("跑");
    }
    protected void useWeapon(){
        fm.fight();
    }
}

而在SwordsMan內直接這樣宣告

class SwordsMan extends Character{
    public SwordsMan(String username){
        super(username);
        fm = new UseSword();
    }
}

這樣劍士要改變攻擊方式就可以直接換成useKnife();
或者要增加一百個角色,也可以直接在介面上增加攻擊方式。
你看出多型的好處了嗎?

參考:
深入淺出JAVA(book)
深入淺出設計模式
歐萊禮網頁http://www.oreilly.com.tw/column_sleepless.php?id=j022
良葛格http://caterpillar.onlyfun.net/Gossip/JavaEssence/InheritanceWhat.html


--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 61.221.115.4
推 lancelotte:寫個簡單又清楚:)                                     12/14 16:59
推 chrisQQ:好文推!                                                12/14 17:24
→ LoveCheer:有些引數傳遞的地方寫反了 還有一些名詞前後不太一致@@   12/14 19:27
                                        感謝提醒!已經修改 不過名詞是指?^^"
→ LoveCheer:^^" 謝謝你花時間修改 ex:  charUseSword 前面沒有宣告   12/14 20:00
→ LoveCheer:我最近也在思考復合跟轉發的問題 還掌握不到精髓所在     12/14 20:02
→ LoveCheer:我在想是不是可以將各職業寫成interface呢?             12/14 20:04
→ LoveCheer:class player extends Character implements 魔法, 劍士  12/14 20:07
→ LoveCheer:這樣就可以有復合職業了(像DQ一樣A_A) (抱怨:推文等好久  12/14 20:09
已經修改好了 感謝 你要考慮is-a 跟 has-a的關係喔!
※ 編輯: givemepass      來自: 61.62.45.162         (12/14 20:28)
推 tomap41017:推強大說明                                           12/14 22:55
推 solomn:推                                                       12/15 09:04
推 lovelycateye:好文推                                             12/15 10:52
→ LoveCheer:我是有在JAVA版看你文章的人@@                          12/15 22:21
→ LoveCheer:回水球變推文@@                                        12/15 22:22
推 LoveCheer:推大大! 太開心了 經由跟大大學習我終於明白了!        12/16 09:49
→ LoveCheer:複合+多型是為了改良 文章前半部使用多型的方式          12/16 09:52
→ LoveCheer:請問是說未來遇到要使用多型時 都盡量改以複合+多型 嗎?  12/16 10:10
推 dream1124:策略模式                                              12/16 12:23
→ givemepass:是策略模式沒錯                                       12/16 14:30
推 computer5566:推                                                 12/16 16:54
推 tkhunter:好文,該m起來                                          12/18 19:31
推 papayamilk:推                                                   04/20 21:17
▋ ie945167 推:借轉 感謝~~~                                        12/07/12
▋ ie965225 推:其實我還是看不太懂 XD                               12/07/14
◤ ie945167 推:哈哈 我也是 不過最近剛好在看有相關的東西 XD         12/07/15

2012年5月19日 星期六

[筆記][C&++] 字串整數轉換

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記][C&++] 字串整數轉換
 時間  2012/05/19 Sat 13:53:13
───────────────────────────────────────

原文網址:

http://edisonx.pixnet.net/blog/post/34060957-%5Bc%26%2B%2B%5D-%E5%AD%97%E4%B8%B
2%E6%95%B4%E6%95%B8%E8%BD%89%E6%8F%9B


節錄:

5. strtol:將字串視為任意進制,轉為長整數

long int strtol ( const char * str, char ** endptr, int base );

ex:
 char buffer[] = "1011";
 long a = strtol(buffer, NULL,2);
// 將 "1001" 視為2進制,轉完之後 a 即為 11




(10.2) sprintf - 浮點數、整數、字串... 轉字串

ex:
int a=0;
float b = 1.2;
char demo[] = "Test";
char dest[200];
sprintf(dest, "%d %f %s", a, b, demo);


--

     接球會"噴"

我             舉球會"歪"              但是!   我算分超強 o(一︿一+)o

                         攻擊會"OUT"

--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-84-252-89-adsl-tao.dynamic.so-net.net.tw 發表

2012年5月13日 星期日

[筆記] 以下程式碼有效率上的問題, 請找出並修改之.

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 以下程式碼有效率上的問題, 請找出並修改之.
 時間  2012/05/13 Sun 21:54:09
───────────────────────────────────────

Question:

using namespace std;
string FindAddr( list<Employee> l, string name )
{
    for (list<Employee>::iterator i = l.begin(); i != l.end(); i++)
    {
        if (*i == name)
        {
            return (*i).addr;
        }
    }
    return “”;
}

-----------------------------------------------------------------------

Answer:

const sing namespace std;
const string* FindAddr(const list<Employee> l, const string name )
{
    for (list<Employee>::iterator i = l.begin(); i != l.end(); ++i)
    {
        if ((*i).addr.compare(name) == 0)
        {
            return &(*i).addr;
        }
    }
    return NULL;
}

-------------------------------------------------------------------------

i++
    temp = (*this); // temp = i
    ++*this;        // i = i + 1
    return *temp;   // return = temp

++i
    ++*this;        // i = i + 1
    return *this;   // return i


m< >:: iterator it

for(it = m.begin() ; it!=m.end() ; it++)
{
    employee& operator == (String& s)
    {
        return this.addr.compare(that);
    }
}

Set, List, Map

http://www.javaworld.com.tw/jute/post/view?bid=5&id=28127&tpg=1&ppg=1&sty=1&age
=0#28127

http://jhengjyun.blogspot.com/2010/09/list-set-map.html

http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=4007

--

     身高不是距離
                   技巧不是問題
                                 只要有"心"
                                             人人都可 定‧三‧米


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-42-130-adsl-TPE.dynamic.so-net.net.tw 發表
▋※ Modify: 2012/05/13 Sun 21:58:35
◣ ie965225 推:這些是? 哪邊看到的呀~?                              12/05/14
▋ ie945167 推:前幾個星期主管出的回家作業,然後在例行會議中討論    12/05/14
▋ ie965225 推:喔喔喔 XD 我也想學 :P                               12/05/15
▋ ie945167 推:ok 的!!!                                            12/05/15
◣ SuperTree 推:不會C / C++ 怎辦                                   12/06/03

[筆記] 在STL中vector與deque提供了相似的功能, 請簡單說明兩者的差異及使用

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 在STL中vector與deque提供了相似的功能, 請簡單說明兩者的差異及使用
 時間  2012/05/13 Sun 21:38:19
───────────────────────────────────────

Q. 在STL中vector與deque提供了相似的功能, 請簡單說明兩者的差異及使用的時機


在STL中提供了vector, list,deque幾種可當作清單使用的資料結構,

他們都是動態增長的

在這三者之中選擇的準則主要是關注插入特性以及對元素的後續訪問要求。

差異

    從後面刪除vector跟deque差不多一樣快

    vector

    表示一段連續的記憶體區域每個元素被順序存儲在這段記憶體中

    對vector 的隨機訪問效率很高(vector::at())。

    (JiangMiao:不是因為它快,而是因為他不慢且安全).

    刪除非末尾元素效率比較低。

    在任意位置而不是在vector 末尾(插入/刪除)元素則效率很低,

    因為它需要把待(插入/刪除)元素右邊的每個元素都拷貝一遍

    (從前面刪除, "後面的都要copy到前面" )。

    一旦太多東西時,vector容器得另覓連續記憶體區塊,而將整個資料搬移

    vector 的動態自我增長越頻繁元素插入的開銷就越大。

    實際上對於小的物件,vector 在實踐中比list效率更高

    deque

    從前後刪除都很快,也表示一段連續的記憶體區域,

    但是與vector 不同的是它支持高效地在其首部插入和刪除元素

    它通過兩級陣列結構來實現一級表示實際的容器第二級指向容器的首和尾

    如果有大量釋放操作,deque將花費更多的時間(和 vector相比)

    list

    表示非連續的記憶體區域,並通過一對指向首尾元素的指標雙向連結起來,

    從而允許向前和向後兩個方向進行遍歷

    在list 的任意位置插入和刪除元素的效率都很高,指標必須被重新賦值,

    但是不需要用拷貝元素來實現移動

    對隨機訪問的支援並不好,訪問一個元素需要遍歷中間的元素,

    每個元素還有兩個指標的額外空間開銷

使用時機

    對這幾種容器類型選擇的一些準則

    如果我們需要隨機訪問一個容器則:vector 要比list 好得多

    如果我們已知要存儲元素的個數則:vector 又是一個比list 好的選擇

    如果我們需要的不只是在容器兩端插入和刪除元素則:list 顯然要比vector 好

    除非我們需要在容器首部插入和刪除元素:否則vector 要比deque 好

    vector

    優先默認選擇的容器

    已經知道vector要很大的szie 你可以一開始就擴充至你要的size,

    這樣就可以減少配置和copy了

    停止擴充後 對iterator的存取率很高的時候

    因為deque的記憶體是分開的 所以他的 iterator的運算會多幾個檢查會比較慢

    所以此時用vector會比較好

    當有大量數據要push_back,記得要vector::reserve().

    deque

    當有大量的首或尾刪除操作.

    或記憶體須不斷配置

    或copy成本很高

    如果你打算用insert或者有pop_front()的需要,使用deque.


使用記憶體圖解

vector
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□

deque
□□□□□□

□□□□□□

□□□□□□

□□□□□□

□□□□□□

list
[ ]□[ ]
[ ]□[ ]
[ ]□[ ]
[ ]□[ ]
[ ]□[ ]
[ ]□[ ]



書上雖說除非有其他不利於 vector 的因素,不然主要還是以使用 vector 為主,

但主管說在他實際使用的經驗上,除非對於「連續記憶體」這點很要求,

不然還是會比較常用 deque,

不過我還是認為看程式需求吧,畢竟這兩個各佔擅場。



--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-42-130-adsl-TPE.dynamic.so-net.net.tw 發表

[筆記] 請簡單說明(或舉例)Deadlock發生的條件

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 請簡單說明(或舉例)Deadlock發生的條件
 時間  2012/05/13 Sun 21:26:52
───────────────────────────────────────

1. 互斥(mutual exclusion):

    每次只能有一個process 可以使用該資源(該資源不允許共享),

    如果另一process 對該資源提出申請,則此提出申請的process 必須等待,

    直到該資源被釋放為止。

    eg. printer、Disk、CPU etc.
    eg. 不具mutual exclusion→Read-only File

1. 佔用及等待(hold and wait):

    系統中至少存在一個process持有某項資源,

    並正在等待獲取別項正被其它process所持有之資源。

    EX:
          (□a)←--╮
        process A   process B
            ╰--→(□b)

        如果 process A, B 已經各自拿到 a 和 b 的資源,

        但 A 還要需要 b資源,且 B也需要 a 資源,但是這些資源都已經被佔住了,

        所以就形成 deadlock。

    那麼如何在程式中避免掉這個問題呢?

    ANS:資源 lock 的順序需相同。

         也就是兩個 process 在 lock 住資源的順序需相同,

         都必須先 lock 住 A 或 B,才能再 lock 另一個資源。

         如此可避免掉 deadlock 的情況。

3. 禁止搶先(不可強取)(no preemption):

    一個資源只能由持有它的process 在該process完成其工作之後自願釋放才行。

4. 循環等待(circular wait):

    必須存在一組正在等待的process{ P0,P1,…,Pn},

    其中P0 正在必須等待P1 持有的某個資源,P1 又等待被P2持有的某個資源,

    最後Pn 也正等待被P0 持有的某個資源。

    process各自佔用一些資源,而彼此互相在等待對方的資源,而形成一個迴圈。



所以為了預防死結的發生,必須針對這四個條件下功夫,

只要一個條件不成立,死結就會不成立。

--

     接球會"噴"

我             舉球會"歪"              但是!   我算分超強 o(一︿一+)o

                         攻擊會"OUT"

--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-42-130-adsl-TPE.dynamic.so-net.net.tw 發表
▋※ Modify: 2012/05/13 Sun 21:27:13

2012年4月4日 星期三

[筆記] 全文轉錄 - 好的遊戲程式系統簡介

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 全文轉錄 - 好的遊戲程式系統簡介
 時間  2012/04/04 Wed 17:53:15
───────────────────────────────────────

好的遊戲程式系統簡介
日前發於DCI的文章...
關於遊戲系統架構的一點淺見

http://forum.dci.org.tw/viewtopic.php?t=519

文章主題: 自問自答: 怎樣才是好的遊戲系統? (一個技術與非技術的討論)
1. 符合玩家需求(非技術性,歡迎參與討論...)

文章主題: 2. 應用物件導向設計系統(技術篇)

文章主題: 3. 最終章 封閉系統但是保持系統擴充性


內容太多了…懶得貼了…有興趣的自己去看吧…0rz

--

     身高不是距離
                   技巧不是問題
                                 只要有"心"
                                             人人都可 定‧三‧米


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

[筆記] 全文轉錄 - 類別的設計

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 全文轉錄 - 類別的設計
 時間  2012/04/04 Wed 17:50:12
───────────────────────────────────────

http://imandrewliao.blogspot.com/2009/01/blog-post_16.html

類別是C++實踐物件導向程式的基礎,一般人對類別設計應該都有了些基本的概念,

這篇想要以系統開發的角度,來強調類別設計與使用的注意事項。


1. 變數封裝

基本上除了特別特別的需求,一般都建議你,把資料放在private區段,

為什麼呢,這樣我才可以設計函式,管控如何存取這些變數值,

避免變數被莫名奇妙的變更,

造成錯誤時,難以追蹤的困擾,

所以一個基本的變數,如果需要被外界存取,那需要一個設定函式,一個讀取函式,

如果不需要,內部使用,那一個都不需要提供。


可以放在protected嘛?基本上,類別設計,建議你忘記有這個區間的存在,為什麼?

protected區間可以被後續繼承類別存取,除非你有特殊用途,否則你埋下了一個炸彈,

你如何知道後續繼成出去的類別,不會胡亂更動其值?

不會又寫了一個函式,把資料public出去?


class NPC
{
    private:
    int m_iHp;  // 生命力,除了初值化外,不應該由外界來操作這個變數
    int m_iAcc; // 物理攻擊準確度,由企劃公式產生,更不應該由外界來操作
};


這樣的寫法,也斷絕了,系統開發時,有人妄想修改資料的念頭,減少將來系統長大時,

潛藏錯誤發生的機會,真的需要處裡內部數值時,再以下列方法處理:

class AStrangeClass
{
    private:
        NPC *m_pNpc;

    public:
        void SetNpc( NPC *npc ){ m_pNpc=npc; }
        NPC *GetNpc(){ return m_pNpc; }
};

關於全域變數呢,基本上建議,寫程式應該要把他視為罪惡,

這意味著有一個完全不能改的機制在那邊,無法控制其變數名稱,

只要跟這個變數有瓜葛的類別,形成了一個盤根糾結的系統,

如同先前我提到的,少用繼承一樣的結果,

將來改其中任何一個地方,潛在需要修改的程式是全部。


可是全域變數一定會用到的,這個地方建議去參考"Design Patterns"的Singleton樣式,

提供了一個將變數全域化,但卻被類別管控的方法。

2. 成員函式設計

成員函式設計,視目的而定,沒有一定的規範(除非專案本身的規定),

這裡建議,請在函式放置位置的時候,多思考一下,要放在哪個區段,

一般的建議是這樣,

如果函式需要被外界存取,放public(廢話...),

不需要的時候,請放private(廢話兩枚)。

是廢話嗎?回去隨便拿一個別人寫的類別看看,

是不是有很多函式被外界呼叫後,如果數值亂填,可能造成系統錯誤之類的?

你也許會反駁說,說明中有寫,這是內部使用的,錯誤是因為外界亂用啊?

可是我要說,設計類別的基本原則,就是讓人容易使用,且不容易出錯,

與其你放任函式public,裡頭寫一大堆exception,

為什麼不把這些函式private起來,錯誤發生率也小多了不是嗎?


常在程式之間流傳的對答:

啊,我知道bug發生原因了,是因為某某某呼叫了我一個函式,

而這個函式是內部使用的,不可以這樣呼叫...,



謹慎考慮多少可以減少潛藏可能發生問題的機會。


3. 注意別人類別的規範

接下來是使用別人類別的注意事項,不要企圖去改變回傳值的屬性,

原因是來自C++轉型的方便,許多人喜歡把參數或是回傳值,

亂轉型,變成其他的類型來操作,

這本是無可厚非的事情,最明顯的就是const回傳值,

但是,系統開發的時候,標明const,就是不希望你去改變這個數值,

嚴重的說來,改變這個值,搞不好會發生嚴重的錯誤,而經常有人就愛去碰:


class A
{
    public:
        const char *GetTypeName(){ return "this is a type"; }
};

void main()
{
    A a;
    const char *ptr=a.GetTypeName();
    strcpy( (char *)ptr, "this is not a type" );
}

編譯都對,執行的時候,就當給你看,所以

(a) 當你看見函式回傳值有const時,不要嘗試去改變回傳值內容

(b) 當函式宣告有throw時,請處理exception

    因為throw表示,函式內可能發生無法預期的錯誤,catch它,

    可以明瞭錯誤的原因,方便除錯

(c) 當類別裡面沒有虛擬函式時,不要嘗試去繼承它

    如果你寫個類別,會給後續類別繼承,你會怎麼寫?一定是在裡面寫一些虛擬函式,

    暗示後來的程式師,這幾個函式可能以後會有其他的寫法,

    如果你寫個類別,沒有任何虛擬函式,那表示什麼?

    不想被別人繼承?功能已經完備?不論哪個答案,都不該再有類別繼承它了。

    除此之外,還有一個原因,虛擬解構式,在繼承情況下,

    解構的記憶體才會正確的釋放,

    你的類別什麼虛擬函式都沒有(含解構式),繼承它,

    未來背負著一個memory leak的風險。

(d) 正確的繼承

    假設,CHuman是個基底類別,CHuman人物會動、可裝備、會說話....,

    假設遊戲中出現了一種東西叫"假人",人類經常習慣用名稱將之歸類為人類的一種,

    所以他該繼承CHuman,然後把所有不能用的功能重寫。

    你做了什麼事?把一個功能齊全的人類別,降級成什麼都不會的假人?

    只因為它長得像人?

    或者應該由一個物品,開始去繼承,增加功能比較適合?


    這裡提一個繼承基本上的判斷標準: 假人是不是一種人?

    意思是,人類會什麼,應該假人也要會什麼,少把類別寫成,假人是人的一種,

    但是他太多不會這不會那時...,大概就是錯誤的繼承關係了。



--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

[筆記] 全文轉錄 - 少用繼承

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 全文轉錄 - 少用繼承
 時間  2012/04/04 Wed 17:46:41
───────────────────────────────────────

http://imandrewliao.blogspot.com/2009/01/blog-post_07.html

物件導向程式設計中,為了提升程式碼的再利用性,提供了繼承這樣的架構給我們使用,

只要看不順眼的類別,就可以用繼承改寫舊功能,達到擴展的目的,範例如下:

class AI
{
    private:
    virtual void FSM(); // AI類別實做FSM
};

class Monster : public AI
{
    private:
    virtual void FSM(); // Monster類別改寫原來的函式
};


但就"程式碼再利用的"目的而言,事實上,我們常用的方法還有一種,

"組成",利用物件彼此間的協力,完成特定工作,

譬如,老闆要求去做一件A、B君都只會做部分功能的工作,

依照繼承的觀念,是否要生成第三個類別C,繼承A、B的能力,

然後改寫部分關聯而增加的程式碼?

然而我們是不是可以換個方向思考,

我可以設計個類別C,工作交代C,C會先請A君做完他會做的,

然後B繼續接手,一樣可以完成工作,

與繼承兩者,事實上複雜度一樣,都需要3個類別,

但是程式維護的小心程度卻是不可相提並論,程式如下


class AI
{
    public:
        void FSM();
};

class TimeControl
{
    public:
        void DoSomething();
};


用繼承:
class Npc:public AI, public TimeControl
{
    void DoSomething(); // 這裡遭遇第一個難關
                        // 多重繼承,要小心AB的成員變數、成員函式設計關係
};

用組成:
class Npc
{
    private:
        AI ai;
        TimeControl tc;

    public:
        void DoSomething(); // 我可以在這裡,靈活運用ai, tc物件的函式
                            // 達成老闆目標
};

你可以看見,往後ABC三個類別的修改,相依性是否會降低很多很多,

說白話,只要AB類別確定功能正常,發生BUG一定就是C,

A類別新增怎樣的功能,不會影響C或B的運作,同理C增減怎樣的函式,AB都不會影響,

而使用繼承呢? 就算你確定ABC三類別功能都正確,卻有可能因為繼承的關係,

造成邏輯錯誤,像是C發生BUG的原因,是因為C修改了一個變數值,

然後是A或B非常重要的變數,不允許由外部變更的

(這裡有個小伏筆,避免修改到別人家的變數,變數請用private封裝...)。


加上有許多程式濫用繼承,一個遊戲專案裡頭隨便都成千上百個類別,

加上彼此繼承的關係,專案像是N坨大肉粽樹,盤根錯節,

某Z類別有BUG,原因卻是在千里之外八竿子打不到關係的A類別造成,

改了A,卻連鎖反應要把某個肉粽樹的函式都要改過,想到就累人啊,

所以,有得選的情形下,請少用繼承多用組成。


再舉個生活上的例子,老闆要設計個簡報在客戶面前推銷商品,為了達到目標,

他需要繼承文書小姐的能力+產品部門對商品規格特色了解的能力+簡報能力+....,

或者,

他可以把公司內文書小姐、產品部經理、行銷推廣部經理叫來,說出需求,

接下來大家就自動會把事情搞定,而老闆則可專心挑毛病去,

愛用繼承的,通常工作累得要死,

愛用組成的,通常是老闆,你選哪個?



--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

[筆記] 全文轉錄 - 類別裡面的const"變數"

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 全文轉錄 - 類別裡面的const"變數"
 時間  2012/04/04 Wed 17:44:20
───────────────────────────────────────

http://imandrewliao.blogspot.com/2009/02/const.html

最近被問到,類別裡面的const變數撰寫方式,乾脆整理const的一些規則,

首先,const應該是常數不是變數,所有的常數只能被初值化,不能被賦值,

所以只能寫在建構式的initial-list中,一般常數的設計方式有以下幾種:


1. 用#define 這個方式是最暴力的,被define的常數本身不隸屬任何類別,

曾經看過這樣的程式碼,寫在程式的任何地方意義都是一樣的:

// 寫在裡面
class ABC
{
    #define PI 3.14159
};

#define PI 3.14159
class ABC
{
};

所以,嚴格說來,這不算"類別裡面的const變數"。


2. 用const修飾的變數
class ABC
{
    const float PI;
};
這種方式只能在建構式的initial-list初值化,錯過這個機會,變數值就不知道是什麼了

ABC::ABC()
: PI(3.14159) // 這裡
{
}

3. 類別內共用的常數變數,可以用static修飾,讓所有物件共用這個"常數"
class ABC
{
    static const int SONG=123;
};

用這種方法可以直接在類別宣告裡面給初值,但是有一個限制,

這種方法僅限於型別是整數的型態(至少VC是這樣的),

如果想要用非整數的型態,要用這樣的寫法

class ABC
{
    static const float PI;
};

在CPP某處加:
    const float ABC::PI=3.14159;

為什麼要加在CPP? 只是因為不能讓"const float ABC::PI=3.14159;"被編譯兩次以上,

如果放在.h有可能發生,而造成編譯錯誤。

4. 使用enum
    class ABC
    {
        enum { SONG=134, STOP=1113 };
    };

用這種方法,只要enum區段合法,任何時候都可以用 ABC::SONG

這樣的寫法來取得其值,不過限制是,enum只支援整數型別...。

這麼多種常數設計方式,我們要如何挑選正確的呢?

以下提出一般常用的原則(記住,只是原則,沒有規定一定要這樣)

1)#define大致上會被丟到歷史的洪流去了,因為本身有很多隱藏的危機,

    在系統寫大的時候,經常造成不知所謂的BUG

2)const變數,每個物件還有機會設定各自的常數值

    class ABC
    {
        private:
            const float PI;

        public:
            ABC(float a)
            :PI(a)
            {
            }
    };

    void main( void )
    {
      ABC abc(100);
      ABC def(200);
    }

3)通常用於類別內,獨一無二的非整數的常數值

4)通常用於類別內,獨一無二的整數的常數值


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

[筆記] i++ 好,還是 ++i 好?

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] i++ 好,還是 ++i 好?
 時間  2012/04/04 Wed 17:41:35
───────────────────────────────────────

不負責任聲明:以下憑印象+google寫成,有錯誤很正常 0rz

              如果有人願意指出錯誤,我會很感謝您的 <(_ _)>

--------------------本文開始分隔線--------------------

下面是我們常用到的迴圈
for(i=0 ; i<? ; i++)
{
    (something)
}

大家常用 i++ 還是 ++i 呢?

其背後的運作模式為何?

T& opertor++ ()
{
    a = i++;
    a = ++i;
}

取址運算子 &
取值運算子 *

i++
    temp = (*this); // temp = i
    ++*this;        // i = i + 1
    return *temp;   // return = temp

++i
    ++*this;        // i = i + 1
    return *this;   // return i

簡單來說,i++ 背後會比 ++i 多複製了一次(我印象中聽到是兩次…0rz)

如果 i++ or ++i 的 i 只是單純的資料型態,如 int

那麼先加或後加就沒什麼太大的差別了

但是如果 i 是代表一個很大的物件

那麼多做的那些動作對於程式執行的效率就會有所影響

因此建議盡可能養成使用 ++i 的寫作習慣 (不過我已經寫了快七年的 i++ 了…0rz)

(對於ints 和 pointer,編譯器最佳化可將這額外的動作去除。但對於較複雜的
 iterator 型別,這個額外動作的潛在代價可能很高。)

--

     身高不是距離
                   技巧不是問題
                                 只要有"心"
                                             人人都可 定‧三‧米


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

[筆記] 變數宣告在什麼位置比較好?

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 變數宣告在什麼位置比較好?
 時間  2012/04/04 Wed 17:38:43
───────────────────────────────────────

不負責任聲明:以下憑印象+google寫成,有錯誤很正常 0rz

              如果有人願意指出錯誤,我會很感謝您的 <(_ _)>

--------------------本文開始分隔線--------------------

以前的 C-style 寫法,希望變數都宣告在函式最一開始的地方

這樣的話,有宣告什麼變數都一目了然,非常的方便

如下所示

void main()
{
    int a, b, c

    a = xxx;

    b = xxx;

    c = xxx;
}

而且不論早宣告還是晚宣告,程式在 compile 的時候都一樣會將全部空間都撥出來給你

例如你整個程式宣告了三個 int 的變數

那麼不論這三個變數在最一開始就宣告了

或是在程式中段或尾段才宣告

compile 所撥出來的空間都是一樣的

那麼宣告在什麼時間點或位置有差別嗎?

經主管說明後,真的有差…

最好是在你要使用這個變數前再宣告,而且盡可能的給予初值

如下所示

void main()
{
    …
    …
    …

    int a = XXX;

    int b = XXX;

    int c = XXX;
}

不過我也不清楚給予初值最主要的目的是什麼,在網路上查了一下,
int x = 10;


int x;
x = 10;

效果是相同的
但後者彈性太大,而且在 code 海中,x=10 很容易被忽略掉。

另一個說法是:

最好在建構式中,給予所有變數初值,避免執行期間產生錯誤,

因為一般而言,C++在除錯版的時候,會自動給定變數初值(0, NULL),而Release版不會

變數值是看當時配置的記憶體資料內容而定

所以典型的程式描述是我Debug版跑起來都沒錯,但是Release版就是會當機...。

回到正題,這樣做有什麼好處呢?

1. 當你在維護程式時,你想要知道變數影響的地方,

    你只要從宣告的地方開始往下找就好,而不需要從函數最一開始的地方開始看。

2. 若宣告在函數開頭的地方,有時會無法給予初值,

    因為有些變數需要程式先執行某段 code 後才能給予初值。

3. 當程式中途有 return 結束的狀況時,如下所示

    void main()
    {
        int a, b, c

        a = xxx;

        return ???;

        b = xxx;

        c = xxx;
    }

    我印象中是說宣告變數時,背後都會去執行某一段 code

    因此如果一開始就把變數都宣告好,也執行了背後那段 code

    但是程式因為 return 結束或其他原因而沒有使用到這個變數

    那麼執行宣告 b, c 的 code 的時間就浪費掉了

    int 這種資料型態可能還好

    但如果是那種大物件的資料型態,花費的時間有時就會很可觀了

--

     接球會"噴"

我             舉球會"歪"              但是!   我算分超強 o(一︿一+)o

                         攻擊會"OUT"

--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

[筆記] const & const_cast

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] const & const_cast
 時間  2012/04/04 Wed 17:35:48
───────────────────────────────────────

不負責任聲明:以下憑印象+google寫成,有錯誤很正常 0rz

              如果有人願意指出錯誤,我會很感謝您的 <(_ _)>

--------------------本文開始分隔線--------------------

在先前的文章中有提到

指定運算元:A::operator =( const A& a ){}

     盡量加上 const     b:因為有加 const 的關係,可以保證在 function 內,
               ↓       ↓                                      B 值不會被更改
T& operator = const (T& b)
                     ↑
                      &:reference 有加的話效率較高?

當我們希望函數中不要更改任何的物件時

可以使用 const 來限制


    void mem_function() const
or
    get_XXX() const



但是有時候我們在函數中不得不去修改某物件的值時該怎麼辦呢?


    get_XXX() const
    {
        if(temp > 0)
        {
            temp = ???;
        }

        return temp
    }

我們可以使用 const_cast

來解除 const 不得更改物件狀態或值的限制


可是,這樣就造成矛盾了不是?

原先我們是為了不要修改到函數裡面的物件狀態而限制為 const

但是現在卻又把它給解除了

這樣不是很容易就會出現 bug 嗎?


所以 const_cast 盡可能不要用,或是完全不要用

可以將要修正的值,宣告成

mutable temp

即可

http://caterpillar.onlyfun.net/Gossip/CppGossip/constAndmuttable.html

而且盡可能的使用 const ,可以讓程式更少出錯且易於維護


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表
▋※ Modify: 2012/04/04 Wed 17:39:05

[筆記] 複製建構式 copy-constructor

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 複製建構式 copy-constructor
 時間  2012/04/04 Wed 17:31:47
───────────────────────────────────────

不負責任聲明:以下憑印象+google寫成,有錯誤很正常 0rz

              如果有人願意指出錯誤,我會很感謝您的 <(_ _)>

--------------------本文開始分隔線--------------------

複製建構式:
A::A(const A &a )
{

}

前一篇文章有提到,C++標準中有幾個函式你不需要寫,C++都會幫你生出來,

而你寫了,就不會幫你產生,複製建構式是其中一個


那什麼時候會需要自己寫一個?

Ans:當有使用指標的時候,不然 copy 的會是 pointer,而不是 data

1. http://www.cnblogs.com/oomusou/archive/2007/01/14/620374.html

當類別資料copy涉及記憶體時,永遠要寫複製建構式與operator =();

複製建構式: A::A(const A &a ){}

指定運算元: A::operator =( const A& a ){}


不然在解構時,同一個運算元會被解構兩次而造成 code 掛掉

2. http://imandrewliao.blogspot.com/2009/01/blog-post_22.html

(第三點的內容)


另外印象中好像有提到在使用 copy-constructor 時

會配合使用 swap function 來做資料的交換

(不過我不知道原因為何…0rz)


如交換 a, b 資料 → a.swap(b)

temp(b)
this → swap(b)
return *this;


另外
    (a = b).function;

    a = b;
    a.function

是相同的

--------------------------------------------------

以下是第一個連結的全文轉貼,避免連結失效

(原創) 哪些地方會用到Copy Constructor和Assignment Operator? (C/C++)

C#、Java都沒有copy constructor,所以這對大部分programmer都很陌生

簡單地說,凡需要copy的地方,就需要copy constructor:


1.由copy-initialization方式建立物件。

    Ex.

    Foo foo1;
    Foo foo2(foo1);

    以上直接使用copy constructor。

    string s = "C++ Primer";
    Foo foo = Foo();

    此時是先由default constructor建立一個temporary object後,

    再由copy constructor將temporary object 『copy』給物件。

2.以by value的方式傳進function和由function return值。

    Ex.
    int Foo(int n);

    只要不是使用by reference的方式,就得使用copy constructor。

3.建立STL container中的element。

    Ex.vector<string> svec(5);

    先由string的default constructor建立一個temporary string object,

    再由string的copy constructor『copy』到vector中每個element。

4.由initialization list建立array。

    Ex.int ia[] = {1, 2, 3};

    先由int default contructor先建立temporary int object,

    再由int的copy contructor『copy』到array中。

    所以copy constructor其實是無所不在的,只是我們從來沒有發現,

    雖然C++ compiler會自己synthesize copy constructor,

    但也允許我們自己定義自己的Copy Constructor,這與C#、Java不同。


而assignment operator呢?

Foo foo1;
Foo foo2;
foo2 = foo1;

以上會執行foo2的assignment operator。


所以簡單的說,copy constructor和assignment operator都在做『copy』的動作,

資料是pointer,也就是動態資料時,就必須重新改寫,

否則只會copy pointer,而不是copy data。

--------------------------------------------------

以下是第二個連結的全文轉貼,避免連結失效


建構式與解構式

預設建構式與解構式有許多的地雷,首先,如先前所說,

C++在你沒有撰寫此兩個函式的時候,會自動生成,

那麼因為建構式支援多載,預設解構式還會生成嗎?

答案是不會的,以下以範例說明:

class ClassHasDefaultConstruct
{

};

class ClassHasNoDefaultConstruct
{
    public:
        ClassHasNoDefaultConstruct(int i){}
};


我們可以注意到第二個範例,有寫建構式但不是預設建構式(沒有任何參數的),

此時C++就放棄幫你建立預設建構式了,

可是有時候,我們在使用STL函式庫的時候,強迫要求你設計預設建構式,

所以ClassHasNoDefaultConstruct這個類別是不能為STL容器所用。


這裡有個小伎倆,把有參數的建構式當成預設建構式用,利用預設參數的方式:

class ClassHasNoDefaultConstruct2
{
    public:
        ClassHasNoDefaultConstruct2(int i=0){}
};

使用建構式有一些注意事項,如果你的類別支援繼承,在建構式中,不可呼叫虛擬函式,

因為,物件建構的順序是:基底類別建構式完成後,然後才是衍生類別建構,

所以在基底類別建構式中呼叫虛擬函式,永遠是基底類別的函式



其次養成習慣,最好在建構式中,給予所有變數初值,避免執行期間產生錯誤,

因為一般而言,

C++在除錯版的時候,會自動給定變數初值(0, NULL),

而Release版不會,

變數值是看當時配置的記憶體資料內容而定,

所以典型的程式描述是我Debug版跑起來都沒錯,但是Release版就是會當機...。

class BadSample
{
    private:
        char *ptr;

    public:
        BadSample(){};
        ~BadSample()
{

if( ptr ) delete ptr; // 當機
};

};


第三是當類別資料copy涉及記憶體時,永遠要寫複製建構式與operator =();

或者,類別不允許這兩個函式的執行,

原因很簡單,以下以程式說明之:

class CopyConstructor
{
    private:
        char *ptr;

    public:
        void ANewFunction()
        {
            ptr=new char[1024];

        }

        ~CopyConstructor()
        {
            if( ptr ) delete[] ptr; // 假設建構式有把ptr=NULL;
        }
};


以下例子解構時都會當機,因為ptr會被釋放多次:

CopyConstructor a;
a.ANewFunction();

CopyConstructor b(a); // b物件刪除的時候,ptr釋放一次,等到a物件刪除時,又一次

CopyConstructor c;
c=a;  // 同上


所以要嗎你把類別這麼寫:

class CopyConstructor
{
    private:
        char *ptr;

    public:
        void ANewFunction()
        {
            ptr=new char[1024];
        }

        CopyConstructor(const CopyConstructor& c )
        {   
            ptr=new char[1024]; // 各自配置各自的記憶體
            memcpy( ptr, c.ptr, 1024 );
        }

        void operator =(const CopyConstructor& c ) // 簡化起見,不討論回傳值
        {   
            ptr=new char[1024]; // 各自配置各自的記憶體
            memcpy( ptr, c.ptr, 1024 );
        }

        ~CopyConstructor()
        {
            if( ptr ) delete[] ptr; // 假設建構式有把ptr=NULL;
        }
};


要嗎你把類別這麼寫:

class CopyConstructor
{
    private:
    char *ptr;

    public:
        void ANewFunction()
        {
            ptr=new char[1024];
        }

        CopyConstructor(const CopyConstructor& c )
        {   
            ASSERT(0);  //給他當機
        }

        void operator =(const CopyConstructor& c ) // 簡化起見,不討論回傳值
        {
            ASSERT(0);  //給他當機
        }

        ~CopyConstructor()
        {
            if( ptr ) delete[] ptr; // 假設建構式有把ptr=NULL;
        }
};


解構式的注意事項是,如果想讓後續類別繼承,

一定要用virtual,讓繼承解構的順序正確,

還有,同建構式般,不可以在解構式中,呼叫虛擬函式,

因為解構是繼承物件先解構,然後才基底物件,

衍生物件已經不見了,基底物件去哪呼叫正確的虛擬函式呢?

--

     身高不是距離
                   技巧不是問題
                                 只要有"心"
                                             人人都可 定‧三‧米


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表
▋※ Modify: 2012/04/04 Wed 17:39:15

[筆記] 如何宣告初始值?

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 如何宣告初始值?
 時間  2012/04/04 Wed 17:09:57
───────────────────────────────────────

不負責任聲明:以下憑印象+google寫成,有錯誤很正常 0rz

              如果有人願意指出錯誤,我會很感謝您的 <(_ _)>

--------------------本文開始分隔線--------------------


一般人在宣告初始值時,大部分都採用下面的方式

T::T()
{
    a = XXX;
    b = XXX;
    c = XXX;
}

也有另一種做法如下

T::T()
: a(120), b(0.3), c(5)
{

}

這兩者有差嗎?以結果來看沒差。
但在執行效率上,後者會較快

因為前者的做法,等於後者做兩次
原因我也沒有辦法說得很清楚
大家可以去看這篇文章
http://imandrewliao.blogspot.com/2009/01/c-1.html
或是去參考 Effective C++ 這本書

重點是,後者在使用上有個限制,就是給初始值的順序要與變數宣告的順序一致
不然給值時會發生錯誤
原因同樣在上述 blog 文章中可以看到

--------------------------------------------------
以下為全文轉貼,避免連結失效

變數初值

程式寫這麼久,以前經常會遇到,"啊? C++不可以這樣嗎?"或者是"這樣寫有差別嗎?"
的驚訝

想說這可能不只是只有我有的感覺,因此把這些東西分享出來,給大家做參考

你也可以參考Effective C++這本書籍:

初值化(Initial)和給定數值(Assign)

(a) 什麼是initial?
    變數宣告的時候,直接給了數值,像是:
    int a=10;

(b) 什麼是Assign?
    變數宣告完後,再另外給定數值,像是:
    int a;
    a=10;

這兩種寫法有差異嗎? 對build-in類型,像是int, float, ...沒有差別

但是對物件而言,就決定了一點點效能上的差別了,我以一個類別A來解釋:

class A
{

}; // 什麼事都沒有的類別


使用assign你必須先宣告一個物件 A a;

此時,物件已經執行了一次建構式,把內部數值初始一次

你也許會問,類別中沒有建構式啊?何來呼叫建構式?

答案是,類別雖然沒有設計建構式

但是C++標準中有幾個函式你不需要寫,C++都會幫你生出來

而你寫了,就不會幫你產生

分別是:
預設建構式: A::A(){}
解構式: A::~A(){}
複製建構式: A::A(const A &a ){}
指定運算元: A::operator =( const A& a ){}

所以當你下A a;a=b;這樣的程式時,已經執行了一次建構式,

之後再一行assign,物件又會執行一次"指定運算元",

所以你跑了兩次物件的函式。

而Initial呢? 看起來A a=b; 這樣的程式碼,

同樣是執行一次建構式,然後再執行一次指定運算元?

答案是,編譯器直接執行複製建構式,所有數值一次搞定

比起Assign快了一倍,也許不多

但是,如果你的物件內部,變數數量超多

一個assign需要執行的指令碼很多,那也許就有點可觀了:)


說到建構式,也許有人會問,他看過以下兩種程式碼,有差別嗎?
A::A()
: a(120), c(0.3)
{
}

A::A()
{
    a=120;
    c=0.3;
}

答案依照結果論而言,沒有差別,但是同樣潛在一點點效能上的差別

首先第一種方法A()後面接著的":a(...." 那行稱為 "member initializer list"

專供給物件內成員變數的初值化,與呼叫基底類別的建構式所用

在這個時候,物件的記憶體還在配置中

C++會一邊配置記憶體,一邊把你指定的數值塞進去

所以,變數給定數值的次數只有一次


而下面那種A::A(){ ... } 的方式呢?

此時記憶體已經配置完成,變數數值已經給定過了一次,而此時又再設定一次

所以跑了兩次數值給定的程序,所以會慢一滴滴。


member initializer list這麼好用,有什麼限制嗎?

有的,指定順序要跟變數宣告的順序一致

原因說過,C++會一邊配置記憶體,一邊給定數值

如果順序不對,以下的程式就可能出錯:

class A
{
    vector buf;
    int size;
    A() : buf(size), size(20)
    ....
};


你希望配置大小為size的vector的陣列,但是在 呼叫buf(size)建構時

size記憶體還沒配置出來,根本不知道size的數值

而,size(20)初值後,buf(size)已經執行過了,所以會發生錯誤

所以正確的順序:

class A
{
    int size;
    vector buf;
    A() : size(20), buf(size)
    ....
};


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

[筆記] 實務分享:當在 free(p) 時,程式出錯,問題何在?

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] 實務分享:當在 free(p) 時,程式出錯,問題何在?
 時間  2012/04/04 Wed 17:04:25
───────────────────────────────────────

不負責任聲明:以下憑印象+google寫成,有錯誤很正常 0rz

              如果有人願意指出錯誤,我會很感謝您的 <(_ _)>

--------------------本文開始分隔線--------------------

主管分享的經驗,我自己連 malloc, free 都沒寫過…悲劇 0rz

主管:「最近遇到一個問題,有個程式在 free(p)的時候掛掉,請問大家,原因為何?」

malloc():動態配置記憶體
free():釋放記憶體

p = malloc(100); //配置100個 byte 的記憶體位置給p,但不做初始化的動作


...
free(p); //掛掉 囧 why?

常見可能的原因:
1. p = NULL
2. p指標飛了,分配時的指向不一樣
3. free(p),p的值不是通過malloc()等函數得到的
4. p已經free過了
還有什麼原因?

我印象中,最後好像是 free 的空間大小與原本 malloc 所配置的大小不一樣
至於是怎麼查出來的呢?
可以看下面的解說

下面是一個記憶體空間

memory cookie   memory
 ______________________
|4 ~ 8 byte  | p      |
 ___________

              ↑
            pointer

(圖畫得不好請見諒…)

在指標(pointer)所指的位置前 4~8 個byte,有所謂的 memory cookie

好像是用來記錄後面的 memory 分配的大小

因此可以用 memory cookie 來判斷 free 的空間大小是否與原本配置的大小相同。

(主管用意在於讓我們知道有所謂的 memory cookie 這種東西的存在可以利用)


題外話,google得來:

malloc 和free 一定要成對出現,有malloc 的,一定要有free,

且注意malloc 分配的大小,防止破壞管理分配記憶體的資訊。

當陣列ar作為參數時,在函數體內,ar只是一個指標,

所以 sizeof(array)=4,而非陣列所含元素個數+1.

--

     接球會"噴"

我             舉球會"歪"              但是!   我算分超強 o(一︿一+)o

                         攻擊會"OUT"

--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-85-0-189-adsl-nei3.dynamic.so-net.net.tw 發表

2012年3月19日 星期一

[筆記] C 語言複雜型別閱讀規則

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] C 語言複雜型別閱讀規則
 時間  2012/03/19 Mon 00:55:42
───────────────────────────────────────

原文出處:
    http://ticore.blogspot.com/2012/03/c-language-complex-declartation.html

文章分享

大家好,我是 Ticore
剛開始接觸 C 語言,發現它的型別宣告真是超級複雜
除了一般型別之外,還有指標、陣列、函式指標
混在一起時真的是非常難懂
書上也沒找到一個系統性的閱讀規則

不斷的嘗試錯誤之後,歸納出來一些心得
首先是 C 語言宣告位置的一些限制


1. 所有型別符號 type specifier 只能放在變數名稱 identifier 左邊,
    e.g. int id;

2. 指標宣告 pointer declarator 只能放在變數名稱 identifier 左邊,
    e.g. int * id;

3. 陣列宣告 array declarator 只能放在變數名稱 identifier 右邊,
    e.g. int id [10];

4. 函式呼叫 function declarator 只能放在變數名稱 identifier 右邊,
    e.g. void id ( int i );

5. 群組運算子 grouping operator 必須包含變數名稱 identifier,
    e.g. int ( * id ) [10];

接下來是閱讀規則


1. 先以變數名稱 identifier 為基準,分為左半部與右半部

2. 先看 grouping 最外層的左半部,再看右半部

3. 左半部是從左到右閱讀,右半部是從右到左閱讀

4. 往內一層 grouping 閱讀,一樣是先看左半部,再看右半部

以此類推...


例子:
    見 blog 文章


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-84-254-78-adsl-kao.dynamic.so-net.net.tw 發表

[筆記] IEEE754 浮點數 轉換

 作者  ie945167 (龍蝦)                                      站內  Lobster
 標題  [筆記] IEEE754 浮點數 轉換
 時間  2012/03/19 Mon 00:18:57
───────────────────────────────────────

原文出處:
    http://edisonx.pixnet.net/blog/post/83095843


Float → Hex


先以 float 為例,假設宣告是

float f;
unsigned u;

要將 f 之記憶體內容拉到 u 裡面去,觀念是,

(1) 先將 float 取位址值                         --->                 &f
(2) 將位址內容轉型成 unsigned* 指標             --->      (unsigned*)&f
(3) 將對 unsigned* 指標做取值(dereference) 動作 --->     *(unsigned*)&f
(4) 再設給 u 值                                 ---> u = *(unsigned*)&f
(5) 再顯示 u 之 16 進位                         ---> printf("%8x\n", u);

u = *(unsigned*)&f;


Hex → Float

DWORD DW_Data;

float f = *(float*)&DW_Data;



PS.

1. 在轉換時會有誤差出現,需注意及特別處理

2. 負數部分我目前沒有試過

3. 上面的 (2) 我還是不懂這樣的做法,背後是怎麼運作的…0rz


--
▅◣ Origin:  謠 言 報  bbs.csie.fju.edu.tw
▋◤ Author: ie945167 從 219-84-254-78-adsl-kao.dynamic.so-net.net.tw 發表

2011年4月6日 星期三

[生涯] 讀應用外語系 想跨考資工 第一步該怎麼做

 作者  jyleef.bbs@ptt2.cc (jyleef.bbs@ptt2.cc)              看板  Lobster
 標題  [生涯] 讀應用外語系 想跨考資工 第一步該怎麼做
 時間  2011/04/06 Wed 01:42:38

作者: ZZZZZZZZZ9 (Z9)
標題: [轉錄]Re: [建議] 讀應用外語系 想跨考資工 第一步該怎麼做?
時間: Tue Apr  5 22:58:20 2011

作者: briancom (小刀) 看板: CareerPlan
─────────────────────────────────────

原文恕刪

線性代數:


內容大致談論矩陣 克拉瑪 還有進階的特徵值


1/3是高中數學的東西




離散數學:


一開始講一些邏輯 還有排列組合 機率之累的


另外的counting的部分屬於資料結構的範圍




接下來的部分懶得打


C --> C++-->資料結構-------->演算法

                    離散數學




由以上圖知道 要會資料結構要先會C語言和C++


沒學過資料結構  演算法會很慘





組合語言---->計算機組織


計組通常用一種教MIPS的語言


修之前要有組合語言的基礎





作業系統................給你一個比較實際的例子


其實人的行為就是作業系統


如果說把你所會做的事情(如讀書 打B 聊MSN...等)各當做一個process的話

那麼~你在電腦前讀書的模式應該會是這樣的...


身為一個學生

一次可以做很多事情是很正常的

所以大學生可以被視為一個具有多工能力的CPU

但是大腦只有一個 也就是說一個人只具有一個CPU

因此應該要使用time-sharing的方法實踐多工...

因此就會變成以下情形...



當你正在執行一個讀書的process時

偶爾你會發現到MSN有人敲你

這個時候電腦就會打一個interrupt給你

這個時候你的大腦即使很想要讀書

卻會因為這個interrupt而強迫中斷你的讀書行為

這個時候

你就會放下你的書本

專心的聊天

而當你執行聊天這個process到一段時間之後

聊天的quantum會用完

(此時通常是發現聊太久了  不讀書會良心不安)



用完之後

就會強迫自己把聊天中斷掉

並且回到讀書這支process

但是好景不常

雖然說應人而異

但通常每個人讀書的quantum都不會太大

一下子就會耗盡

(此是依照人的專心程度不同 range從10分鐘到1小時不等)

這時又會有interrupt打進來

原因不盡相同 有些人是打B 有些人是看plurk

不管怎麼說

你讀書的process就是會被打斷


如此的反覆

你所執行的讀書總是會被外在行為打斷



而有趣的是

其他外在的事情不管在平常是多麼的不重要

一旦你開始執行讀書的程式時

其他事情的priority都會比讀書這件事情還要高

也就是說

當你在讀書的時候

即使是你平常不會做的事情

你都有可能會搶在讀書之前去做




例如說你可能會開始整理自己的寢室

開始去丟你平常死都不拿去丟的垃圾...等等

但你就是不會讀書!!!



不過這些都還好

至少程式執行到最後讀書終究是會看完(不管你是努力理解還是走馬看花)

最慘的就是

你想要執行睡覺這個程式...

這可就不得了了

睡覺這個程式的priority應該是最高的...

一旦執行下去就沒完沒了


進入一種無我的境界...

此時人體的大腦CPU就會專心執行睡覺這支程式

完全不理會其他程式的中斷

這時不管是室友找你吃宵夜

或是隔天有期中考

都不管了



而在執行此程式之後

讀書process會進入ready queue而變成ready的狀態

但睡覺這個程式很奇怪

它具有不理會quantum的特性

也就是說 當你quantum快要用完時後

(通常情況是你假設你只會睡10分鐘)

該CPU會自動幫這程式補血!!!

額外多加quantum以便繼續執行

(此情形類似於  啊...再多睡10分鐘好了)

因此...通常你睡覺都會睡超過1小時以上

而此時讀書程式就會被晾在一旁

最糟糕的情況是


你一睡就睡到隔天考試前...

在這種情況下

你讀書的程式就會發生starvation 也就是餓死的情況

它永遠都等不到CPU回來執行它

而此時...也就宣告你期中考死亡的結局了~~~




不過說了這麼多

給了我一點啟示

CPU在執行多支程式的時候

浪費最多的就是在context switch

也就是在程式之間轉換當中的空檔 這是最浪費時間的

因此

只要你從頭到尾都只執行一隻程式 就不會有context switch發生

也就不會浪費時間

因此

只要你在讀書時不去理會其他事情

讀書效率就會提高







給按end的你



看完後你還想考資工所嗎????


不過像資結 演算之類的科目要先去學校修過才行


因為考試歸考試 PROJECT寫不出來 就算進去資工所


能不能出來也是個問題


--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.113.121.253
※ 編輯: briancom        來自: 140.113.121.253      (03/29 19:32)
推 sonicnaru:你在教os嗎XDDDD                                       03/29 19:33
推 say2003y:淺顯易懂......太屌了                                   03/29 19:36
推 dakkk:資工也不是只有OS的東西吧^^                                03/29 19:56
→ dakkk:演算法才是難的地方                                        03/29 19:56
→ dakkk:第一段有寫演算法 不好意思 我習慣按PgDn ^^                 03/29 19:58
推 loginonly:幹 寫得真好 你一定是讀電機or資工的XDDD                03/29 20:13
→ howshou:你講的太有趣了, 會騙到一堆人繳補習費後才發現真相。      03/29 20:34
推 r393501:生動給推 XDDDD                                          03/29 20:34
推 austin12340:最近正在鑽研中...你比喻的很好!!!!!                  03/29 20:47
推 Nilo:XDDDD                                                      03/29 20:51
推 anneju:太好笑了XDDD                                             03/29 21:04
推 apiod:不愧是113的XDDDDD                                         03/29 21:21
推 whika:XDDDDDDDDDDDD                                             03/29 21:49
推 owenx:這個譬喻真是生動有力啊!都可以開班授課了XD                03/29 21:49
推 ambermrs:我最近在讀os,看到原po生動地的講解~~真的是太厲害了!!   03/29 22:00
→ ambermrs:<另外,提一下,我以前是應用外語,後來改走資管,走得   03/29 22:01
→ ambermrs:有點辛苦,因為程式這思維太需要邏輯了!(真的要深思啊!     03/29 22:02
推 lyeni:寫的太棒了吧!!!!!                                         03/29 23:45
推 fbiwbi:你的os比喻應該是要有修過os才能看的懂吧                   03/29 23:58
推 rainley:我竟然看得懂XDDDDDDDD                                   03/30 00:20
推 RX0UNICORN:XD                                                   03/30 00:55
推 xcxc:睡覺應該是non-mask interrupt,直行期間可以不理會其他中斷    03/30 01:21
推 red0210:後面暴走了  還有第26行  之"累"的 是故意的嗎XD           03/30 11:30
推 slow1235:it's cool!!                                            03/30 13:44
推 tkhunter:看完這篇文章,OS功力大增!                             03/30 15:45
→ howshou:這篇不能m吧, 會誤導到很多新手以為資工所很好考。         03/30 16:39
推 owenx:像我上面幾篇那篇板主就沒M,怕誤導別人以為戲劇系很偉大XD   03/30 17:13
推 compassk:←非資訊背景但可以懂7成XD                              03/30 21:42
推 EVASUKA:這教得比九成以上的教授還生動了                          03/31 15:33
推 Desney:^^                                                       03/31 17:47
推 zxbnm:GREAT                                                     03/31 19:36
推 SHOOTA:推這篇XDDD                                               04/01 04:40
→ anedo:太生動了..                                                04/01 11:55
推 monar:真有畫面 不推不行XDD                                      04/01 19:09
推 basta:原po說的內容,心理學上也有做過實驗,證明一心多用很沒效率  04/03 01:50
→ basta:就是在工作轉換中,需要花點時間跟精力抽出後再投入          04/03 01:51


--
   ★         ◢    ◣                                                ★★★
  ★          ◥◣◢◤         嗚哇哈哈 嗚哇 !  我是神龍 ZZZZZZZZZ9! ★★
             ◥ .\/. ◤  ★★
    ★★    __◥ ≡ ◤__ ★★  ★★      ie945167 你有什麼特別的願望呢  ★
   ★★★  ◢◣◥..◤◢◣      ★★                       ★
    ★★   ▼▼﹌  ﹌▼▼      ★★    集滿七顆龍珠      ★★   願望就會實現

--
※ 發信站: 批踢踢兔(ptt2.cc)
◆ From: 114.45.176.168
→ jskblack:被OS荼毒太深了 XDD                                  推 04/06 01:36
▋ ie945167 推:OS的比喻好好笑 XDD                                  11/04/06