搜尋此網誌
2012年1月26日 星期四
const 指標變數修飾標的
int num1;
num1 是個 int
int *num2;
num2 是個指標, 指標所指的位置是個 int
const int *num3;
num3 是個指標, 指標所指的位置是個 const int,所以你不可以透過num3這個指標去改變num3所指到的位址的值.
int * const num4;
num4 是個 const 指標,你不能改變 num4 所指的位址,但可以透過num4這個指標去改變 num4所指到位址的值.
const int * const num5;
num5 是個 const 指標, 指標所指的位置是 const int,所以不能改變num5所指的位址,亦不能透過num5這個指標去改變num5所指到的位址的值.
判斷方法:
以 * 為基準點作劃分.
例如
const int * const num;
如果 const 出現在*的左邊, 表示 num 這個指標所指到的位址的值是 const (不能透過num這個指標去改變num所指到的位址的值)
如果 const 出現在*的右邊, 表示 num 這個指標是 const (不能改變num這個指標所指到的位址)
2011年11月22日 星期二
C++ 型別轉換
C++ 的型別轉換有以下四種轉換方式 分別是 static_cast, dynamic_cast,reinterpret_cast 以及const_cast .分述如下:
static_cast
可用於轉換基底類別指標為衍生類別指標,也可用於傳統的資料型態轉換。
舉例來說,在指定的動作時,如果右邊的數值型態比左邊的數值型態型態長度大時,
超出可儲存範圍的部份會被自動消去,例如將浮點數指定給整數變數,則小數的部份會被自動消去
Ex: int num1 = 0; double num2 = 1.11; num1 = static_cast<int>(num2);dynamic_cast
使用static_cast(甚至是傳統的C轉型方式)將基底類別指標轉換為衍生類別指標,
這種轉型方式稱為強制轉型,但是在執行時期使用強制轉型有危險性,
因為編譯器無法得知轉型是否正確.
Ex:
#include <iostream>
struct Base {
virtual ~Base() {}
virtual void name() {}
};
struct Derived: Base {
virtual ~Derived() {}
virtual void name() {}
};
struct Some {
virtual ~Some() {}
};
int main()
{
Some *s = new Some;
Base* b1 = new Base;
Base* b2 = new Derived;
Derived *d1 = dynamic_cast<Derived*>(b1);
Derived *d2 = dynamic_cast<Derived*>(b2);
Derived *d3 = dynamic_cast<Derived*>(s);
Base *d4 = dynamic_cast<Base*>(s);
std::cout << "'b1' points to 'Derived'? : " << (bool) d1 << '\n';
std::cout << "'b2' points to 'Derived'? : " << (bool) d2 << '\n';
std::cout << "'s' points to 'Derived'? : " << (bool) d3 << '\n';
std::cout << "'s' points to 'Base'? : " << (bool) d4 << '\n';
}
Output:
i = 4
type::i = 4
'b1' points to 'Derived'? : false
'b2' points to 'Derived'? : true
's' points to 'Derived'? : false
's' points to 'Base'? : false
reinterpret_cast用於將一種型態的指標轉換為另一種型態的指標,例如將char*轉換為int*
Ex:
#include <iostream>
using namespace std;
int main() {
int i;
char* str = "test";
i = reinterpret_cast(str);
cout << i << endl;
return 0;
}
const_cast用於一些特殊場合可以覆寫變數的const屬性,利用cast後的指標就可以更改變數的內部。
const_cast是一危險的轉型"唯一使用"的場合是當物件資料屬性為const時但有時卻必須傳遞給非const參數的成員函數或一般全域函數時才可用它來移除const屬性因為唯有如此才可
符合型態相同(形式引數(參數))但使用時必須確定此轉型後不會藉此來更改其資料否則可能會造成無法預測的結果!!!
Ex:
#include <iostream>
struct type {
type():i(3)
{}
void m1(int v) const
{
const_cast<type*>(this)->i = v;
}
int i;
};
int main()
{
type t;
t.m1(4);
std::cout << "type::i = " << t.i << '\n';
}
Output:
type::i = 4
2011年11月4日 星期五
如何紀錄一個矩形區塊(rb)在一個矩形範圍(ra)的相對位置 ,並依比例還原至絕對位置及範圍
1. 利用 QPointF 分別紀錄rb在ra當中的左上角的點,與右下角的點的相對位置
ex:
// 假設變數定義如下
// s_topleft_x (rb 左上角x在ra的絕對位置)
// s_topleft_y (rb 左上角y在ra的絕對位置)
// s_bottomright_x (rb 右下角x在ra的絕對位置)
// s_bottomright_y (rb 右下角y在ra的絕對位置)
// picWidth (ra的寬)
// picHeight (ra的高)
QPointF topPF = QPointF((double)s_topleft_x/picWidth, (double)s_topleft_y/picHeight);
QPointF bottomPF = QPointF((double)s_bottomright_x/picWidth, (double)s_bottomright_y/picHeight);
2. 設定 QRectF
ex:
QRectF newRect(topPF, bottomPF);
以上newRect就記錄了rb在ra中的相對位置及範圍了
B.還原相對位址
// 假設變數定義如下
// newPicWidth (欲還原ra的寬)
// newPicHeight (欲還原ra的高)
// newRect (之前紀錄的rb的相對位置的矩形範圍)
QRect mRect;
mRect.setX((int)(((double)newPicWidth)* newRect.x()));
mRect.setY((int)(((double)newPicHeight)* newRect.y()));
mRect.setBottom((int)(((doublenewPicHeight)* newRect.bottom()));
mRect.setRight((int)(((double)newPicWidth)* newRect.right()));
以上 mRect 就是 rb在新的ra當中的絕對位置及範圍了
2011年9月29日 星期四
在Qt中實現多語系的功能
ex:
QMessageBox msgb(this);
msgb.setWindowTitle(tr("Illegal"));
msgb.setText(tr("Illegal product!"));
msgb.setIcon(QMessageBox::Warning);
msgb.exec();
2. 在.pro檔中,設定預計產生的翻譯檔語系名稱及其位置 (本例名稱為 test.pro)
ex:
TRANSLATIONS += ./translations/test_en.ts ./translations/test_tw.ts ./translations/test_jp.ts ./translations/test_cn.ts
3. 執行 lupdate 產生 .ts檔
ex:
lupdate test.pro
4. 使用 Qt Linguist 編輯.ts檔
5. 使用 lrelease 產生 .qm檔
ex:
lrelease ./translations/*.ts
6. copy .qm檔到你所指定的目錄
ex: cp -ax ./translations/*.qm /home/daniel/language/qm/*
7. 在主程式 main當中,依據需求載入你所需的qm檔
ex:
QTranslator translator, defTrans;
QLocale g_sysLocale;
//繁體中文
translator.load("test_tw", "/home/daniel/language/qm");
defTrans.load("qt_zh_TW", "/home/daniel/language/qm");
app.installTranslator(&translator);
app.installTranslator(&defTrans);
g_sysLocale = QLocale(QLocale::Chinese, QLocale::Taiwan);
// 簡體中文
/*
translator.load("test_cn", "/home/daniel/language/qm");
defTrans.load("qt_zh_CN", "/home/daniel/language/qm");
app.installTranslator(&translator);
app.installTranslator(&defTrans);
g_sysLocale = QLocale(QLocale::Chinese, QLocale::China);
*/
QLocale::setDefault(g_sysLocale);
MainWindow mainWin(0);
mainWin.setLocale(g_sysLocale);
mainWin.start();
return app.exec();
8. 所有包在 QObject::tr()中的字串,就會依照所載入的qm檔,做文字置換動作
2011年9月19日 星期一
如何在QT中將QVariant轉換成定義在QtGui中的資料型態 (如 QColor...等),或自定型別
如要轉換,方法式將其轉換至指定的樣版型別,如下所示,(PS:自訂型別方法相同):
QColor color = variant.value
不過在轉換之前最好先用canConvert()的函式來檢查是否可提供該型別轉換,如下所示:
if (variant.canConvert
QColor color = variant.value
}
反向轉換(如把QColor轉成QVariant)則和一般的型態(非定義在QtCui或自訂型別)是一樣的
2011年6月14日 星期二
在QT中如何在QApplication中攔截X11 的事件
class NewApplication : public QApplication
{
Q_OBJECT
public:
NewApplication(int& argc, char ** argv);
virtual bool x11EventFilter(XEvent *event);
private:
};
bool NewApplication::x11EventFilter(XEvent *event)
{
switch (event->type) {
case ButtonPress:
{
if (event->xbutton.button == 2) {
// 攔截中鍵 ... 寫你要的動作
}
}
case ButtonRelease:
case KeyPress:
case KeyRelease:
case MotionNotify:
};
return NewApplication::x11EventFilter(event);
}
2. 在mail裡面把建立QApplication改成建立NewApplication
QApplication app(argc, argv);
改成
NewApplication app(argc, argv);
3. start app
app.exec();
2011年3月18日 星期五
台灣新三寶
2010年6月23日 星期三
如何將整數值放入Char陣列中,並還原其值
主要是用在通訊系統,一般而言我們透過序列埠傳送資料時只能傳送BYTE array的資料,所以當我們要傳送一個整數的資料時,就必須將整數資料放在BYTE array中傳送.
但要如何將整數資料放在 BYTE array中呢?
一般方法有兩種
1. 轉成16進制字元陣列 (較節省空間,但運算較複雜)
2. 採用BCD編碼傳送 (較直覺,但浪費空間)
16進制字元陣列:
例如有一個值為 1454591794 其16進制為 0x56B34F32;
我們可以宣告一個char的陣列其大小為4 , 如 char hexStr[4]來儲存; (4個byte剛好為一個int)
其內容分別為 hexStr[0] = 0x32, hexStr[1] = 0x4F, hexStr[2] = 0xB3, hexStr[3] = 0x56
反之我們可以使用下面的方法將 16進制char陣列 還原成int的值
unsigned int tt = (unsigned int) hexStr[0];
unsigned int t2;
tt <<= 24;
t2 = (unsigned int) hexStr[1];
tt |= (t2 << 16);
t2 = (unsigned int) hexStr[2];
tt |= (t2 << 8);
t2 = (unsigned int) hexStr[3];
tt |= t2;
其中 tt就是我們要的整數值
BCD方式如下:
假設值為 1454591794
我們可以將其拆解為 0x14 , 0x54, 0x59, 0x17, 0x94
我們可以宣告一個char的陣列其大小為5 , 如 char hexStr[5]來儲存
其內容分別為
hexStr[0] = 0x94, hexStr[1] = 0x17.........hexStr[4] = 0x14
還原整數方法如下:
char Buf[11];
sprintf(Buf, "%02x%02x%02x%02x%02x",
(unsigned char) hexStr[4],
(unsigned char) hexStr[3],
(unsigned char) hexStr[2],
(unsigned char) hexStr[1],
(unsigned char) hexStr[0]);
int i = atoi(Buf);
其中 i就是我們要的整數值
2010年4月25日 星期日
肚皮舞者
2010年4月18日 星期日
走向共和
2010年4月7日 星期三
Returning values by value, reference, and address
Return by value
Return by value is the simplest and safest return type to use. When a value is returned by value, a copy of that value is returned to the caller. As with pass by value, you can return by value literals (eg. 5), variables (eg. x), or expressions (eg. x+1), which makes return by value very flexible.
Another advantage of return by value is that you can return variables (or expressions) that involve local variables declared within the function. Because the variables are evaluated before the function goes out of scope, and a copy of the value is returned to the caller, there are no problems when the variable goes out of scope at the end of the function.
1.int DoubleValue(int nX)2.{3. int nValue = nX * 2;4. return nValue; // A copy of nValue will be returned here5.} // nValue goes out of scope hereReturn by value is the most appropriate when returning variables that were declared inside the function, or for returning function arguments that were passed by value. However, like pass by value, return by value is slow for structs and large classes.
Return by reference
Just like with pass by reference, values returned by reference must be variables (you can not return a reference to a literal or an expression). When a variable is returned by reference, a reference to the variable is passed back to the caller. The caller can then use this reference to continue modifying the variable, which can be useful at times. Return by reference is also fast, which can be useful when returning structs and classes.
However, returning by reference has one additional downside that pass by reference doesn’t — you can not return local variables to the function by reference. Consider the following example:
1.int& DoubleValue(int nX)2.{3. int nValue = nX * 2;4. return nValue; // return a reference to nValue here5.} // nValue goes out of scope hereSee the problem here? The function is trying to return a reference to a value that is going to go out of scope when the function returns. This would mean the caller receives a reference to garbage. Fortunately, your compiler will give you an error if you try to do this.
Return by reference is typically used to return arguments passed by reference to the function back to the caller. In the following example, we return (by reference) an element of an array that was passed to our function by reference:
01.// This struct holds an array of 25 integers02.struct FixedArray2503.{04. int anValue[25];05.};06. 07.// Returns a reference to the nIndex element of rArray08.int& Value(FixedArray25 &rArray, int nIndex)09.{10. return rArray.anValue[nIndex];11.}12. 13.int main()14.{15. FixedArray25 sMyArray;16. 17. // Set the 10th element of sMyArray to the value 518. Value(sMyArray, 10) = 5;19. 20. cout << sMyArray.anValue[10] << endl;21. return 0;22.}This prints:
5
When we call Value(sMyArray, 10), Value() returns a reference to the 10th element of the array inside sMyArray. main() then uses this reference to assign that element the value 5.
Although this is somewhat of a contrived example (because you could access sMyArray.anValue directly), once you learn about classes you will find a lot more uses for returning values by reference.
Return by address
Returning by address involves returning the address of a variable to the caller. Just like pass by address, return by address can only return the address of a variable, not a literal or an expression. Like return by reference, return by address is fast. However, as with return by reference, return by address can not return local variables:
1.int* DoubleValue(int nX)2.{3. int nValue = nX * 2;4. return &nValue; // return nValue by address here5.} // nValue goes out of scope hereAs you can see here, nValue goes out of scope just after its address is returned to the caller. The end result is that the caller ends up with the address of non-allocated memory, which will cause lots of problems if used. This is one of the most common programming mistakes that new programmers make. Many newer compilers will give a warning (not an error) if the programmer tries to return a local variable by address — however, there are quite a few ways to trick the compiler into letting you do something illegal without generating a warning, so the burden is on the programmer to ensure the address they are returning will be to a valid variable after the function returns.
Return by address is often used to return newly allocated memory to the caller:
01.int* AllocateArray(int nSize)02.{03. return new int[nSize];04.}05. 06.int main()07.{08. int *pnArray = AllocateArray(25);09. // do stuff with pnArray10. 11. delete[] pnArray;12. return 0;13.}Conclusion
Most of the time, return by value will be sufficient for your needs. It’s also the most flexible and safest way to return information to the caller. However, return by reference or address can also be useful, particularly when working with dynamically allocated classes or structs. When using return by reference or address, make sure you are not returning a reference to, or the address of, a variable that will go out of scope when the function returns!
摘自: http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address
2010年3月5日 星期五
What is heap and stack?
What is heap and stack?
The stack is a place in the computer memory where all the variables that are declared and initialized before runtime are stored. The heap is the section of computer memory where all the variables created or initialized at runtime are stored.
What are the memory segments?
The distinction between stack and heap relates to programming. When you look at your computer memory, it is organized into three segments:
- text (code) segment
- stack segment
- heap segment
The text segment (often called code segment) is where the compiled code of the program itself resides. When you open some EXE file in Notepad, you can see that it includes a lot of "Gibberish" language, something that is not readable to human. It is the machine code, the computer representation of the program instructions. This includes all user defined as well as system functions.
Now let's get to some details.
What is stack?
The two sections other from the code segment in the memory are used for data. The stack is the section of memory that is allocated for automatic variables within functions.
Data is stored in stack using the Last In First Out (LIFO) method. This means that storage in the memory is allocated and deallocated at only one end of the memory called the top of the stack. Stack is a section of memory and its associated registers that is used for temporary storage of information in which the most recently stored item is the first to be retrieved.
What is heap?
On the other hand, heap is an area of memory used for dynamic memory allocation. Blocks of memory are allocated and freed in this case in an arbitrary order. The pattern of allocation and size of blocks is not known until run time. Heap is usually being used by a program for many different purposes.
The stack is much faster than the heap but also smaller and more expensive.
Heap and stack from programming perspective
Most object-oriented languages have some defined structure, and some come with so-called main() function. When a program begins running, the system calls the function main() which marks the entry point of the program. For example every C, C++, or C# program must have one function named main(). No other function in the program can be called main(). Before we start explaining, let's take a look at the following example:
int x; /* static stack storage */
void main() {
int y; /* dynamic stack storage */
char str; /* dynamic stack storage */
str = malloc(50); /* allocates 50 bytes of dynamic heap storage */
size = calcSize(10); /* dynamic heap storage */
When a program begins executing in the main() function, all variables declared within main() will be stored on the stack.
If the main() function calls another function in the program, for example calcSize(), additional storage will be allocated for the variables in calcSize(). This storage will be allocated in the heap memory segment.
Notice that the parameters passed by main() to calcSize() are also stored on the stack. If the calcSize() function calls to any additional functions, more space would be allocated at the heap again.
When the calcSize() function returns the value, the space for its local variables at heap is then deallocated and heap clears to be available for other functions.
The memory allocated in the heap area is used and reused during program execution.
It should be noted that memory allocated in heap will contain garbage values left over from previous usage.
Memory space for objects is always allocated in heap. Objects are placed on the heap.
Built-in datatypes like int, double, float and parameters to methods are allocated on the stack.
Even though objects are held on heap, references to them are also variables and they are placed on stack.
The stack segment provides more stable storage of data for a program. The memory allocated in the stack remains in existence for the duration of a program. This is good for global and static variables. Therefore, global variables and static variables are allocated on the stack.
Why is stack and heap important?
When a program is loaded into memory, it takes some memory management to organize the process. If memory management was not present in your computer memory, programs would clash with each other leaving the computer non-functional.
Heap and stack in Java
When you create an object using the new operator, for example myobj = new Object();, it allocates memory for the myobj object on the heap. The stack memory space is used when you declare automatic variables.
Note, when you do a string initialization, for example String myString;, it is a reference to an object so it will be created using new and hence it will be placed on the heap.
摘自: http://www.maxi-pedia.com/what+is+heap+and+stack2010年1月17日 星期日
真是至理名言阿...
機會,就像老二一樣,緊握就會變大!
生活就像是被強姦,反抗不了就學著享受!
學習就像嫖妓,出錢又出力!
工作就像輪姦,如果你不行,就換另一個人來做!
社會就像手淫,全部的事情都要靠自己的雙手去解決!
發薪水就像是月經,一個月不來那麼一次總覺得不能安心!
兄弟就像保險套,插多大的洞都幫你罩著!
就算要fuck,起初也要有fu!
就算是lover,最後還是有個over!
就算是Believe,中間還是有個lie!
承諾,就像一句幹你娘,人人都會說,卻沒人做得到!
2009年12月26日 星期六
自來水博物館遊記
2009年11月6日 星期五
如何執行外部程式,並取得其標準輸出資料
例如,我們要在程式內部執行 ls 這個命令,並取得其結果,我們可以這麼做
// header file ----------
QProcess pls;
// cpp ------------------
connect(&pls, SIGNAL(readyReadStandardOutput()), this, SLOT(sl_readPlsOutput()));
pls.start("ls", QStringList() << "-al");
if (!pls.waitForStarted())
printf("wait for pls\n");
if (!pls.waitForFinished())
printf("wait for pls\n");
// slot -------------------
sl_readPlsOutput()
{
QByteArray tmpArray;
tmpArray = pls.readAllStandardOutput();
}
2009年10月20日 星期二
如何利用QT來取得系統設定
如果我們是用QT開發的話,可以用以下的方式
1. 利用QFile以文字檔的方式開啟設定檔
2. 將設定檔中的資料一行一行的讀出
範例1: 判斷系統是否存在特定型號的DVD
bool isFindDVD()
{
QFile procDVD("/proc/scsi/sg/device_strs");
if (!procDVD.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QString line;
while(1) {
line = QString(procDVD.readLine());
if (line.isEmpty()) break;
eprintf("isFindDVD = %s\n", qPrintable(line));
if ((line.contains("ATAPI")) && (line.contains("DVD"))) {
return true;
}
}
return false;
}
範例2: 取得 CPU的型號
QString cpuModel()
{
QFile procCPU("/proc/cpuinfo");
if (!procCPU.open(QIODevice::ReadOnly | QIODevice::Text))
return "";
QString line;
while (1) {
line = QString(procCPU.readLine());
if (line.isEmpty()) break;
if (line.contains("model name")) {
return line;
}
}
return "";
}
2009年10月9日 星期五
Function template
Function template最簡單的的定義方法如下:
template
MyType min (MyType a, MyType b) {
return (a < b) ? a : b;
}
其中 < class MyType >, 代表是 template 的參數列, 意即 MyType的型別是可經由傳入的參數改變其型別.
例如, 我們如下的方式呼叫
min(10, 100); // int, int
則產生的函式實體為
int min (int a, int b) {
return (a < b) ? a : b;
}
因為,傳入的參數型態為整數,所以 MyType 的型態就變成了 int.
假設我們呼叫的方式為
min(10.0, 100.0); //double, double
則產生的函式實體為
double min (double a, double b) {
return (a < b) ? a : b;
}
當然, 參數列的參數,不以一個為限,它可以有多個參數
如:
template
Type1 findMax(Type1 a, Type2 b)
{
return ( a > b) ? a : b;
}
假設我們呼叫的方式為
findMax(10, 100.0); // int, double
則產生的函式實體為
int findMax(int a, double b) {
return ( a > b) ? a : b;
}
其中 Type1被 int取代,而 Type2 被double取代.
--------------------------------------------------
然而參數列的參數,不僅僅是要型別參數,也可以是非型別參數,所謂的非型別參數所代表的即為一個數值,這個數值在template的定義式中式一個常數
如:
template
Type min(Type (&arr) [size]) {
………
}
其中的 int size 即為非型別參數
假設我們已如下的方法呼叫
Int i;
int ia[] = {1, 2, 3};
i = min(ia);
則產生的函式實體為
int min(int (&arr) [3]) {
………
}
其中 Type被 int取代, 而 size 被3取代 (因為 ia這個這個陣列有三個元素).
2009年9月29日 星期二
Function Pointer(C)、Delegate(C#) 和Function Object(C++)
Abstract
Function Pointer(C)、Delegate(C#)和Function Object(C++)這三個其實是一樣的功能,所以在此一併討論。
Introduction
function pointer是C語言中最高級的機制,大概很多人還沒上到這裡已經學期末了,所以不少C語言工程師根本不知道C語言有function pointer;而C#的delegate大抵跟C語言的function pointer功能相同,所以很多書說delegate是物件導向的function pointer;C++的function object功能則比function pointer略強,還可配合泛型使用。
為什麼會需要function pointer、delegate、function object這種機制呢?源於一個很簡單的想法:『為什麼我們不能將function也如同變數一樣傳進另外一個function呢?』,C語言的解決方式是,利用pointer指向該function,將該pointer傳入另外一個function,只要將該pointer dereference後,就如同存取原function一樣。C#解決的方式是,將function包成delegate object,傳入另外一個function。C++的解決方式是,利用class或struct將function包成object,傳入另外一個 function。
一個很簡單的需求,想個別列出陣列中,所有奇數、偶數、和大於2的數字,若使用傳統方式,而不使用function pointer,則寫法如下
1#include
2
3using namespace std;
4
5void printArrayOdd(int* beg, int* end) {
6 while(beg != end) {
7 if ((*beg)%2)
8 cout << *beg << endl;
9
10 beg++;
11 }
12}
13
14void printArrayEven(int* beg, int* end) {
15 while(beg != end) {
16 if (!((*beg)%2))
17 cout << *beg << endl;
18
19 beg++;
20 }
21}
22
23void printArrayGreaterThan2(int* beg, int* end) {
24 while(beg != end) {
25 if ((*beg)>2)
26 cout << *beg << endl;
27
28 beg++;
29 }
30}
31
32int main() {
33 int ia[] = {1, 2, 3};
34
35 cout << "Odd" << endl;
36 printArrayOdd(ia, ia + 3);
37
38
39 cout << "Even" << endl;
40 printArrayEven(ia, ia + 3);
41
42 cout << "Greater than 2" << endl;
43 printArrayGreaterThan2(ia, ia + 3);
44}
執行結果
Odd
1
3
Even
2
Greater than 2
3
以功能而言沒有問題,但每個function都要做迴圈與判斷,似乎重覆了,而且將來若有新的判斷,又要copy整個迴圈,然後改掉判斷式,若能將迴圈與判斷式分離,若日後有新的判斷式,只要將該判斷式傳進來即可,這就是function pointer概念。
使用C語言的Function Pointer
1/**//*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : FuntionPointer.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use function pointer
7Release : 05/01/2007 1.0
8*/
9#include
10
11using namespace std;
12
13typedef bool (*predicate)(int);
14
15bool isOdd(int i) {
16 return i%2? true : false;
17}
18
19bool isEven(int i) {
20 return i%2? false : true;
21}
22
23bool greaterThan2(int i) {
24 return i > 2;
25}
26
27void printArray(int* beg, int* end, predicate fn) {
28 while(beg != end) {
29 if ((*fn)(*beg))
30 cout << *beg << endl;
31
32 beg++;
33 }
34}
35
36int main() {
37 int ia[] = {1, 2, 3};
38
39 cout << "Odd" << endl;
40 printArray(ia, ia + 3, isOdd);
41
42 cout << "Even" << endl;
43 printArray(ia, ia + 3, isEven);
44
45 cout << "Greater than 2" << endl;
46 printArray(ia, ia + 3, greaterThan2);
47}
執行結果
Odd
1
3
Even
2
Greater than 2
3
第13行
typedef bool (*predicate)(int);
宣告了predicate這個function ponter型別,指向回傳值為bool,參數為int的function,值得注意的是(*predicate)一定要括號刮起來,否則 compiler會以為是bool*,我承認這個語法很奇怪,但若仔細想想,若我是C語言發明者,我應該也是這樣定語法,因為也沒其他更好的語法了:D。
這個範例將判斷式和迴圈分開,日後若有新的判斷式,只要新增判斷式即可,funtion pointer提供了一個型別,讓參數可以宣告function pointer型別
void printArray(int* beg, int* end, predicate fn) {
如此我們就可以將function傳進另外一個fuction了。
使用C#的Delegate
C#是個物件導向的語言,為了提供類似function pointer的機制,提出了delegate概念,delegate英文是『委託、代表』,表示可以代表一個function,可以將delegate想成物件導向的function pointer。
1/**//*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : Delegate.cs
5Compiler : Visual Studio 2005 / C# 2.0
6Description : Demo how to use delegate
7Release : 05/01/2007 1.0
8*/
9
10using System;
11
12class main {
13 public delegate bool predicate(int i);
14
15 public static bool isOdd(int i) {
16 return (i % 2) > 0? true : false;
17 }
18
19 public static bool isEven(int i) {
20 return ((i % 2) > 0? false : true);
21 }
22
23 public static bool greaterThan2(int i) {
24 return i > 2;
25 }
26
27 public static void printArray(int[] arr, int size, predicate fn) {
28 for(int i = 0; i != size; ++i) {
29 if (fn(arr[i]))
30 Console.WriteLine(arr[i].ToString());
31 }
32 }
33
34 public static void Main() {
35 int[] ia = {1, 2, 3};
36
37 Console.WriteLine("Odd");
38 printArray(ia, 3, new predicate(isOdd));
39
40 Console.WriteLine("Even");
41 printArray(ia, 3, new predicate(isEven));
42
43 Console.WriteLine("Greater than 2");
44 printArray(ia, 3, new predicate(greaterThan2));
45 }
46}
執行結果
Odd
1
3
Even
2
Greater than 2
3
整個C#程式和C語言程式幾乎是一對一對應,定義function pointer型別變成了13行
public delegate bool predicate(int i);
表示predicate是一個delegate型別,代表一個迴傳為bool,參數為int的function。
而原來宣告function pointer型態的參數,則改成delegate型態
public static void printArray(int[] arr, int size, predicate fn) {
使用C++的Function Object
function object也稱為functor,用class或struct都可以,因為function object是利用constructor和對operator()做overloading,而這些都是public的,所以大部分人就直接使用 struct,可少打public:這幾個字。
1/**//*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : FuntionObject.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use function object
7Release : 05/01/2007 1.0
8*/
9#include
10
11using namespace std;
12
13template
14struct isOdd {
15 bool operator() (T i) {
16 return i%2? true : false;
17 }
18};
19
20template
21struct isEven {
22 bool operator() (T i) {
23 return i%2? false : true;
24 }
25};
26
27template
28struct greaterThan2 {
29 bool operator() (T i) {
30 return i > 2;
31 }
32};
33
34template
35struct greaterThanAny {
36 T _val;
37 greaterThanAny(T n) : _val(n) {}
38 bool operator() (T i) {
39 return i > _val;
40 }
41};
42
43
44template
45void printArray(T beg, T end, U fn) {
46 while(beg != end) {
47 if (fn(*beg))
48 cout << *beg << endl;
49
50 beg++;
51 }
52};
53
54int main() {
55 int ia[] = {1, 2, 3};
56
57 cout << "Odd" << endl;
58 printArray(ia, ia + 3, isOdd
59
60 cout << "Even" << endl;
61 printArray(ia, ia + 3, isEven
62
63 cout << "Greater than 2" << endl;
64 printArray(ia, ia + 3, greaterThan2
65
66 cout << "Greater than any" << endl;
67 printArray(ia, ia + 3, greaterThanAny
68}
執行結果
Odd
1
3
Even
2
Greater than 2
3
Greater than any
2
3
13行
template
struct isOdd {
bool operator() (T i) {
return i%2? true : false;
}
};
使用了template,不過並非必要,只是顯示function object可以搭配template使用,而使用的技巧只是將function內的東西搬到operator()內。
34行
template
struct greaterThanAny {
T _val;
greaterThanAny(T n) : _val(n) {}
bool operator() (T i) {
return i > _val;
}
};
是function object優於function pointer和delegate之處,由C語言和C#的範例可知,我們只能寫一個greaterThan2()的判斷式,若今天需求改變成 greaterThan3,則又得再寫一個判斷式了,但因為function object是透過struct和class,別忘了struct和class還有個constructor,所以能藉由constructor對 class做初始化,因此才能寫出greaterThanAny(),大於多少只要當成constructor帶入即可,而operator()的寫法一樣不變。
Conclusion
C語言、C#、C++皆提供了『將函數傳到另外一個函數』的機制,function pointer和delegate相當類似,而funtion object則功能更強。這裡澄清一個觀念,很多人認為function object就是為了要使用STL的algorithm才使用,這是標準的錯誤觀念,這是果而非因,因為STL的algorithm使用了 function object的方式,所以我們才去配合,並不是只用在這個地方,事實上,我們自己的也可以使用function object,而且其比function pointer優越之處就在於function object多了constructor,所以比function pointer彈性更大。













