Posted by: kevinlin on: 六月 4, 2009
仿函數跟callback函數很類似,但是有本質上的不太一樣
仿函數似乎就是把Struct或Class假裝成一個函式的樣子。
舉個例,有這樣的一個struct
struct test
{
int a;
int b;
}
有個sort排列要求按a的大小來排,就是
struct sortfunction
{
bool operator()( const test &ta, const test &tb )
{return ta.a < tb.a; }
}
接著就能這樣使用了
std::vector<test > v;
std:: sort( v.begin(), v.end(), sortfunction() )
為什麼不把sortfunction直接寫成一個Function就好,也是可以,但是以類或者結構來處理可以得到更多彈性。
仿函數(functor)的優點
如果可以用仿函數實現,那麼你應該用仿函數,而不要用CallBack。原因在於:
仿函數可以不帶痕跡地傳遞上下文參數。而CallBack技術通常使用一個額外的void*參數傳遞。這也是多數人認為CallBack技術醜陋的原因。
更好的性能。
仿函數技術可以獲得更好的性能,這點直觀來講比較難以理解。你可能說,CallBack函數都寫成inline了,怎麼會性能比仿函數差?我們這裡來分析下。我們假設某個函數func(例如上面的std::sort)調用中傳遞了一個CallBack函數,那麼可以分為兩種情況:
func是inline函數,並且比較簡單,func呼叫最後被展開了,那麼其中對CallBack函數的呼叫也成為一普通函數呼叫(而不是通過函數指標的間接呼叫),並且如果這個CallBack函數很簡單,那麼也可能同時被展開。在這種情形下,CallBack函數與仿函數性能相同。
func是非inline函數的話,或者比較複雜而無法展開(例如上面的std::sort,我們知道它是快速排序,函數因為存在遞回而無法展開)。此時CallBack函數作為一個函數指標傳入,他的程式碼也是無法被展開。而仿函數則不同。雖然func本身複雜不能展開,但是func函數中對仿函數的呼叫是編譯器編譯期間就可以確定並進行inline展開的。因此在這種情形下,仿函數比之於CallBack函數,有著更好的性能。並且,這種性能優勢有時是一種無可比擬的優勢(對於std::sort就是如此,因為元素比較的次數非常巨大,是否可以進行inline展開導致了一種雪崩效應)。
仿函數(functor)不能做的?
話又說回來了,仿函數並不能完全取代CallBack函數所有的應用場合。例如,我在std::AutoFreeAlloc中使用了CallBack函數,而不是仿函數,這是因為AutoFreeAlloc要容納異質的解構函數,而不是只支持某一種類的解構。這和模板(template)不能在同一個容器中支持異質類型,是一樣的。
Posted by: kevinlin on: 八月 29, 2008
手動備份log檔案的bat檔案
這樣可以在檔名上加上時間日期
REM 取得日期
FOR /F “tokens=1-4 delims=/ ” %%a IN (“%date%”) DO (
SET _MyDate=%%a%%b%%c%%d
)
REM 取得時間
FOR /F “tokens=1-4 delims=:.” %%a IN (“%time%”) DO (
SET _MyTime=%%a%%b%%c%%d
)
copy .\1\game.log .\紀錄檔\log_%_MyDate%_%_MyTime%(1).log
copy .\2\game.log .\紀錄檔\log_%_MyDate%_%_MyTime%(2).log
copy .\3\game.log .\紀錄檔\log_%_MyDate%_%_MyTime%(3).log
copy .\4\game.log .\紀錄檔\log_%_MyDate%_%_MyTime%(4).log
Posted by: kevinlin on: 八月 29, 2008
要避免程式被多重開啟,其實最簡單的是FindWindow,但是FindWindow只能找設定好的標題或者Class名字對固定的名稱有用,如果你的標題欄會變動的話那就只能投降了。
比較好的方法就是讓他們互斥…
只要在winmain下面加上這段
HANDLE m_hOneInstance;
m_hOneInstance = ::CreateMutex(NULL, FALSE, "MyProgram");
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
AfxMessageBox("已經開啟一個程式了");
return false;
}
就只能開啟一次了…
Posted by: kevinlin on: 八月 29, 2008
剛把VC6的一個專案轉到VS2005
發生的一個奇妙的錯誤
Linking…
nafxcwd.lib(afxmem.obj) : error LNK2005: “void * __cdecl operator new(unsigned int)” (??2@YAPAXI@Z) already defined in LIBCMTD.lib(new.obj)
nafxcwd.lib(afxmem.obj) : error LNK2005: “void __cdecl operator delete(void *)” (??3@YAXPAX@Z) already defined in LIBCMTD.lib(dbgdel.obj)
這種錯誤是MFC的library跟C的library衝突,要重新設定link的順序,先nafxcwd.lib再libcmtd.lib
所以就是去專案[屬性頁]裡面找到[連接器]\[輸入] 先在忽略特定程式庫打 nafxcwd.lib libcmtd.lib ,把這兩個忽略掉。
再到其他相依性,打nafxcwd.lib libcmtd.lib,他就會照這個順序去link了。。。
Posted by: kevinlin on: 八月 29, 2008
取得檔案大小還蠻重要的,但是一般取得頂多只能2GB,超過2GB的檔案要怎麼辦??
下面的方法1~3可正確取得2GB以下的FileSize,4跟5兩種方法可以正確取得2GB以上的FileSize
method1
unsigned long GetFileLength( FILE * fileName)
{
unsigned long pos = ftell(fileName);
unsigned long len = 0;
fseek ( fileName, 0L, SEEK_END );
len = ftell ( fileName );
fseek ( fileName, pos, SEEK_SET [...]
Posted by: kevinlin on: 六月 18, 2008
我覺得做DLL這東西很有趣,程式執行到一半我們可以呼叫某個DLL裡面的某個function,我想寫個小範例在這邊。
DLL的載入大致上分兩種
1 、 隱式連結(Implicitly Link)(也叫載入期動態連結 Load-Time Dynamic Linking)
這種是屬於靜態載入,就是指,程式編譯時會預留函式的空間。所以當程式被Windows的loader載入到記憶體中後,loader會自動檢查執行檔中所有的import function的定義,把所有需要的DLLs都載入到記憶體裡面。
優點:
1、 使用DLL裡面的Function的方法,就像是包含在自己程式裡面的function一樣可以使用。
2 、 動作較為簡單,載入的方法跟尋找DLL的動作由作業系統負責處理,應用程式不用調整也無法去干涉。
缺點:
1 、 當找不到DLL時,程式就不能開啟(就是常見的找不到xxx.dll)。
2 、 編譯時需要先include DLL的宣告檔(通常是.h)。
3 、 隨著載入的DLLs增加,載入應用程式的速度會便慢。
4 、 若遇到不同品牌 的C++編譯器時靜態載入可就沒有這麼簡單處理了,因為當函式經過Calling Conventions的處理後,若要使用其他品牌編譯器所致造出的DLL須得大動干戈才行。
2 、 顯式連結(Explicit Linking)
跟他的名字一樣,就是在程式執行的過程中動態載入DLL,用完了就馬上釋放DLL
優點:
1 、DLL只要需要時才會載入到記憶體中,可以更有效的使用記憶體。
2 、 應用程式開始的時候不用先載入DLL,所以載入速度比上面的方法快。
3 、 編譯時不須額外include DLL函式的宣告檔。
4 、 讓我們可以更清楚DLL的載入流程。
缺點:
要多花一些程式碼來處理載入的過程!
我要介紹的是Explicit Linking
下面是test.cpp,他會產生一個test.dll
#include <iostream>
#include <Windows.h>
using namespace std;
__declspec( dllexport ) int AddFunction(int a, int b)
{ return a+b; }
__declspec( dllexport ) int SubFunction(int a, [...]
Posted by: kevinlin on: 六月 17, 2008
前面寫了這篇,感覺很亂,再來深入探討一次
看了這篇後,我覺得用組合語言來敘述差異似乎更加的清楚明白
__cdecl 之前說 由呼叫者來調整堆疊
//函式 foo(int arg1, int arg2, int arg3)
push arg3
push arg2
push arg1
call foo
add sp, 12 //相當於 pop; pop; pop
__stdcall 之前說 由被呼叫者調整堆疊,這樣的做法可以節省程式的大小
//函式 foo(int arg1, int arg2, int arg3)
push arg3
push arg2
push arg1
call foo
//stack 的清除工作會在 foo 函式內完成
上面兩個差異MSDN有告訴我們:
The __cdecl calling convention creates larger executables [...]
Posted by: kevinlin on: 六月 12, 2008
我的程式裡warning C4996大概有400多個,回想以前在VS2003時,一個warning都沒有,真是個美好的時代。
發生的warning像下面這樣
warning C4996: ’sprintf’: This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS
這個錯誤是VS2005後,為了安全性而發生的錯誤,當然可以直接無視,不過大概是有三種方法
第一種就是直接無視他
#pragma warning(disable:4996)
或
#define _CRT_SECURE_NO_DEPRECATE 1
上面這個方法就可以很簡單的無視,不過注意要放在pre-compile header file “stdafx.h”最前面,要在還沒有include任何檔案之前。
第二種就是
直接使用Security CRT functions。(在後面會詳述)
第三種就是用
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
這個定義會自動幫你使用 Security CRT functions,但是warning一樣會出現,所以可以1、3混搭
方法一自然是不建議啊!!人家都說有security issure了,你還無視他的存在,那你當機是應該的。
VS2005為了讓我們建構更安全的應用程式,針對一些像是gets、sprintf、strcpy…等等C Run-time Library的操作,提供了比較安全一點的函式。大家稱他為Security Enhancements in the CRT
為什麼要這樣呢? 來看一個簡單的範例
MSDN上說C Run-time Library裡最不可靠的函式就是 gets,舉個例:
char buffer[10] = { 0 };
gets(buffer);
第一行宣告Buffer並將他初始化為0。(建議將所有變數初始化為明確的值,可以避免不必要的意外)。
接著gets會獨進一行你輸入的東西,這方法有甚麼問題呢??問題在,C-style陣列傳入到function中,是傳遞第一個element的指標。所以gets這個function會把char[]當成char*,因此他就沒有一個資訊來知道這個buffer大小了。
這都不是很重要,重要的來了,gets裡他假設這個buffer是無大小限制的(就是UINT_MAX),他只會把你輸入的東西丟進去buffer裡面,想做「緩衝區溢滿」攻擊的人就開始高興了。
所以,很多原始的C Run-time Library都有一樣的問題,缺乏一個驗證的數字,所以到了VS2005,它說「現在他們已經不適用了,該用我的好東西了。」
因為這些function是在以前效能主導的時代所寫的,我們現在已經是安全主導的年代了,這些老舊的東西微軟提供了一些新函式來取代,很簡單他在後面加了 _s ,例如 gets ==> [...]
Posted by: kevinlin on: 二月 18, 2008
書的作者跟譯者都很有名氣,而且這本是第四版了
有很厲害的人幫他推薦
全世界超過450,000名程式員以本書學習C++ !—STEVEVINOSKI, 首席工程師,Product Innovation, IONA Technologies
本書將龐大而複雜的語言化繁為簡—JUSTINSHAW, 資深成員of Technical Staff, Electronic Programs Division, The Aerospace Corporation
本書不僅能讓新手成長且提早飛奔,並且能讓他們以良好的編程風格完成此事。—NEVINLIBER, 資深首席工程師(自1988即開始C++ 開發生涯)
就是大家都知道的C++ Primer第四版
其實我大部分的書都會買原文的,但是有幾本例外,以這本來說我個人覺得侯捷翻譯的很好,侯捷自己寫的一本MFC深入淺出,更是學Windows程式必讀的一本書。C++Primer第三版時,就已經創下很高的銷售紀錄了 作者來頭很大,幾乎跟C++的爸爸一樣有名,C++的爸爸(Bjarne Stroustrup)也寫一本The C++ Progrmming Language,也是一本不錯的書,
作者群由最傑出的C++ 專家組成。Stanley B. Lippman現任Microsoft Visual C++團隊架構師,最早曾於1984與C++ 創造者Bjarne Stroustrup共事於貝爾實驗室(Bell Labs)。他也曾為迪士尼(Disney)動畫團隊及DreamWorks公司工作過,曾任噴射推進實驗室(Jet Propulsion Laboratories)顧問。他的其他書籍包括《Inside the C++ Object Model》(Addison-Wesley,1996)。Josée Lajoie曾在IBM Canada’s C/C++ 編譯器開發團隊任職,亦曾在ISO C++ 標準委員會服務數年,擔任ISO基礎語言事務委員會(core language working group)主席,同時也是《C++ Report》的專欄作家。Barbara E. Moo是一位獨立顧問,擁有25年軟體經驗。她在AT&T和 Stroustrup及Lippman密切合作管理複雜的C++ 開發專案。Moo和Andrew Koenig共同為Addison-Wesley出版公司寫了兩本書:《Accelerated C++》(2000) 和《Ruminations on [...]
Posted by: kevinlin on: 二月 14, 2008
之前有一篇不規則視窗六種方法,其實標題不是下的很好,應該是不規則圖形繪製,那一篇主要就是繪製圖形時,將alpha=0的給去除掉。
這一篇才是真正的不規則視窗的實作。
用
int SetWindowRgn(
HWND hWnd, // handle to window 指定的窗口
HRGN hRgn, // handle to region 輪廓
BOOL bRedraw // window redraw option 呼叫後是否重繪
);
HRGN是由多個點構成的一個資料結構,所以可以做成任何形狀
ex.HRGN rgn = CreateEllpticRgn(0,0,200,100)就有一個橢圓形的region了
先介紹我自己寫的方法:
設計個演算方法取得某張圖的HRGN
假設有一張圖叫做他Image好了
最近的回應