講到物件導向,其實就是把可以reuse的東西包起來,使他可以reuse,包起來不只是只能寫成一個function,您只要是一段程式碼可以重新呼叫,不管您怎樣包都可以,例如您可以裝在class裡或者是namespace裡也可以
我們舉我們在學校第一個學的演算法,也就是bubble sort(氣泡排序法)
當我們要處理一串數字依序排列,就要使用到他,下面會附一個我測試的程式碼壓縮檔在裡面,可以直接使用C++ Builder
首先我們先拉幾個欄位,使他可以輸入數字,這裡是示範,只使用五個
也就是左邊五個欄位輸入數字後,按下button,會依序排列在右邊
首先我們先定義接口,也就是將處理的程式碼拆成三段
/////////////////////////
將欄位存入陣列
處理片段
將處理結果存回欄位
/////////////////////////
這邊假設看的人都知道C++ Builder宣告一個陣列,或配置一塊記憶體,是不會初始化內容的,也就是裡面的值不會都是0,其實講細節要弄成影片比較好懂,儘管影片錄的不好,但是可以做一連貫的說明,其實是比較好的,這邊沒有弄成影片,所以就講得比較簡化一點
#define n 5
void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
int src[n];
// 對陣列初始化
for(unsigned int i = 0;i < n; ++i)
{
src[i] = 0;
}
// 也可用memset處理
//memset(src, 0, n * sizeof(int));
// 讀入值
src[0] = atoi(AnsiString(Edit1->Text).c_str() );
src[1] = atoi(AnsiString(Edit2->Text).c_str() );
src[2] = atoi(AnsiString(Edit3->Text).c_str() );
src[3] = atoi(AnsiString(Edit4->Text).c_str() );
src[4] = atoi(AnsiString(Edit5->Text).c_str() );
///////////////////////////
// 由小排到大
for(unsigned int j = 0; j < n; ++j)
{
for(unsigned int i = j + 1; i < n; ++i)
{
if(src[j] > src[i])
{
// 交換
int temp = src[j];
src[j] = src[i];
src[i] = temp;
}
}
}
///////////////////////////
Edit6->Text = src[0];
Edit7->Text = src[1];
Edit8->Text = src[2];
Edit9->Text = src[3];
Edit10->Text = src[4];
}
如這樣排列,程式碼會比較好懂,我們必須分段處理並且加入適當的註解,這樣才比較清楚,也方便後面要做的動作
這樣是由小排到大,這我已經確認過了
也就是
/////////////////////////
將值讀入陣列
處理
存回欄位
/////////////////////////
這樣基本上就達到排序的功能了
再來我們演算法寫好後,通常為了往後方便(就是以後可以reuse),所以要將它物件化,一般用function就可以了
#define n 5
void bubble_sort(int* src)
{
for(unsigned int j = 0; j < n; ++j)
{
for(unsigned int i = j + 1; i < n; ++i)
{
if(src[j] > src[i])
{
// 交換
int temp = src[j];
src[j] = src[i];
src[i] = temp;
}
}
}
}
void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
int src[n];
// 對陣列初始化
for(unsigned int i = 0;i < n; ++i)
{
src[i] = 0;
}
// 也可用memset處理
//memset(src, 0, n * sizeof(int));
// 讀入值
src[0] = atoi(AnsiString(Edit1->Text).c_str() );
src[1] = atoi(AnsiString(Edit2->Text).c_str() );
src[2] = atoi(AnsiString(Edit3->Text).c_str() );
src[3] = atoi(AnsiString(Edit4->Text).c_str() );
src[4] = atoi(AnsiString(Edit5->Text).c_str() );
///////////////////////////
bubble_sort(src);
///////////////////////////
Edit6->Text = src[0];
Edit7->Text = src[1];
Edit8->Text = src[2];
Edit9->Text = src[3];
Edit10->Text = src[4];
}
如上,您看這樣就變成一個可以reuse的函式,假設您要排五個數字,就可以用這個函式來處理,往後您只要將函式存在一個cpp,帶走就可以用了,就不用每次都在重寫,達到reuse的作用
那這個函式只能排五個,我們要是要排10個或20個呢,一個函式好不好用,在於它的彈性,也就是目前寫法他只能處理五個,再多就不能用了,這種東西就是太死,所以不是那麼好,因此做了一些修改
#define n 5
void bubble_sort(int* src, int m)
{
for(unsigned int j = 0; j < m; ++j)
{
for(unsigned int i = j + 1; i < m; ++i)
{
if(src[j] > src[i])
{
// 交換
int temp = src[j];
src[j] = src[i];
src[i] = temp;
}
}
}
}
void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
int src[n];
// 對陣列初始化
for(unsigned int i = 0;i < n; ++i)
{
src[i] = 0;
}
// 也可用memset處理
//memset(src, 0, n * sizeof(int));
// 讀入值
src[0] = atoi(AnsiString(Edit1->Text).c_str() );
src[1] = atoi(AnsiString(Edit2->Text).c_str() );
src[2] = atoi(AnsiString(Edit3->Text).c_str() );
src[3] = atoi(AnsiString(Edit4->Text).c_str() );
src[4] = atoi(AnsiString(Edit5->Text).c_str() );
///////////////////////////
/*
// 由小排到大
for(unsigned int j = 0; j < n; ++j)
{
for(unsigned int i = j + 1; i < n; ++i)
{
if(src[j] > src[i])
{
// 交換
int temp = src[j];
src[j] = src[i];
src[i] = temp;
}
}
}
*/
//bubble_sort(src);
bubble_sort(src, n);
///////////////////////////
Edit6->Text = src[0];
Edit7->Text = src[1];
Edit8->Text = src[2];
Edit9->Text = src[3];
Edit10->Text = src[4];
}
N已經用掉了,所以我換成m,那個函式就可根據定義的n處理不同數量變數
其實這樣彈性不夠,現在是由小排到大,那要是我要由大排到小呢,我們再做這樣的修改
void bubble_sort(int* src, int m, bool direct) // 0是由小排到大 1是由大排到小
{
for(unsigned int j = 0; j < m; ++j)
{
for(unsigned int i = j + 1; i < m; ++i)
{
if( (src[j] > src[i] && !direct) || (src[j] < src[i] && direct) )
{
// 交換
int temp = src[j];
src[j] = src[i];
src[i] = temp;
}
}
}
}
void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
int src[n];
// 對陣列初始化
for(unsigned int i = 0;i < n; ++i)
{
src[i] = 0;
}
// 也可用memset處理
//memset(src, 0, n * sizeof(int));
// 讀入值
src[0] = atoi(AnsiString(Edit1->Text).c_str() );
src[1] = atoi(AnsiString(Edit2->Text).c_str() );
src[2] = atoi(AnsiString(Edit3->Text).c_str() );
src[3] = atoi(AnsiString(Edit4->Text).c_str() );
src[4] = atoi(AnsiString(Edit5->Text).c_str() );
///////////////////////////
/*
// 由小排到大
for(unsigned int j = 0; j < n; ++j)
{
for(unsigned int i = j + 1; i < n; ++i)
{
if(src[j] > src[i])
{
// 交換
int temp = src[j];
src[j] = src[i];
src[i] = temp;
}
}
}
*/
//bubble_sort(src);
//bubble_sort(src, n);
bubble_sort(src, n, 0);
///////////////////////////
Edit6->Text = src[0];
Edit7->Text = src[1];
Edit8->Text = src[2];
Edit9->Text = src[3];
Edit10->Text = src[4];
}
這樣function可以做兩種方向的處理了,往後就會非常方便了
下面我們要做一些轉換,將它換成vector,因為我這邊要改成動態無限擴增,並簡化函式,目前需要三個欄位,如果換成vector,那基本上我們只需要兩個欄位,因為vector可以查容器的大小(size())
由於我程式碼要存成範例,因此我再另外拉一組介面,重新弄一遍
void bubble_sort(vector<int>& src, bool direct) // 0是由小排到大 1是由大排到小
{
int m = src.size();
for(unsigned int j = 0; j < m; ++j)
{
for(unsigned int i = j + 1; i < m; ++i)
{
if( (src[j] > src[i] && !direct) || (src[j] < src[i] && direct) )
{
// 交換
int temp = src[j];
src[j] = src[i];
src[i] = temp;
}
}
}
}
void __fastcall TForm2::BitBtn2Click(TObject *Sender)
{
vector<int> src;
// 讀入值
src.push_back( atoi(AnsiString(Edit11->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit12->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit13->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit14->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit15->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit16->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit17->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit18->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit19->Text).c_str() ) );
src.push_back( atoi(AnsiString(Edit20->Text).c_str() ) );
///////////////////////////
bubble_sort(src, 0);
///////////////////////////
Edit21->Text = src[0];
Edit22->Text = src[1];
Edit23->Text = src[2];
Edit24->Text = src[3];
Edit25->Text = src[4];
Edit26->Text = src[5];
Edit27->Text = src[6];
Edit28->Text = src[7];
Edit29->Text = src[8];
Edit30->Text = src[9];
}
您看這樣就可以處理10筆了,這邊有個小技巧,可以用列舉的方式,來處理,往後您只要對陣列新增欄位名稱,就可以做處理了
為了程式碼簡潔,我們再拉個button,總之後面我會附一個專案檔,打開就可以研究了
void __fastcall TForm2::BitBtn3Click(TObject *Sender)
{
vector<int> src;
struct Object_Mapping
{
TEdit* src;
TEdit* dst;
};
Object_Mapping om[] =
{
{Edit11, Edit21},
{Edit12, Edit22},
{Edit13, Edit23},
{Edit14, Edit24},
{Edit15, Edit25},
{Edit16, Edit26},
{Edit17, Edit27},
{Edit18, Edit28},
{Edit19, Edit29},
{Edit20, Edit30},
};
int k = sizeof(om) / sizeof(Object_Mapping);
src.clear();
// 讀入值
for(int i = 0; i < k; ++i)
{
int value = atoi( AnsiString(om[i].src->Text).c_str() );
src.push_back( value );
}
///////////////////////////
bubble_sort(src, 0);
///////////////////////////
for(int i = 0; i < k; ++i)
{
om[i].dst->Text = src[i];
}
}
這邊用到一個列舉的小技巧,您可照綠色文字的方式建立一個對應,
也就是來源對應輸出,我們得到物件的指標後,可以用
int k = sizeof(om) / sizeof(Object_Mapping);
取得他的size,然後我們就可以用for迴圈處理了,基本上要再加一組欄位,
只要將對應的指標如下處理,非常的方便
struct Object_Mapping
{
TEdit* src;
TEdit* dst;
};
Object_Mapping om[] =
{
{Edit11, Edit21},
{Edit12, Edit22},
{Edit13, Edit23},
{Edit14, Edit24},
{Edit15, Edit25},
{Edit16, Edit26},
{Edit17, Edit27},
{Edit18, Edit28},
{Edit19, Edit29},
{Edit20, Edit30},
{Edit31, Edit32},
};
這是用C++的好處,我不確定C#是否有這種東西可以使用(因為C#沒有指標),或許有,或許沒有,
而且C#是沒有vector的,不太方便就是了
上面程式碼有點效率問題,就是vector在pushback時,要是超出他已經配置的大小,會重新配置記憶體,並搬移到新的記憶體位置(內部處理動作)
http://pingyeh.blogspot.tw/2011/09/stl-vector.html
至於我們怎麼知道他有做這個動作,其實很簡單,我們知道配置記憶體要時間,
所以我們測量您程式片段所需的時間就可以看出,這以後我再示範,總之記得
Vector如果預先就知道您要push_back的大小,要事先reserve,如下
void __fastcall TForm2::BitBtn3Click(TObject *Sender)
{
vector<int> src;
struct Object_Mapping
{
TEdit* src;
TEdit* dst;
};
Object_Mapping om[] =
{
{Edit11, Edit21},
{Edit12, Edit22},
{Edit13, Edit23},
{Edit14, Edit24},
{Edit15, Edit25},
{Edit16, Edit26},
{Edit17, Edit27},
{Edit18, Edit28},
{Edit19, Edit29},
{Edit20, Edit30},
//{Edit31, Edit32},
};
int k = sizeof(om) / sizeof(Object_Mapping);
// 將整個陣列丟掉
vector<int>().swap(src);
// 預配置陣列大小
src.reserve(k);
// 由於每個值都會蓋過去,所以我們不對vector初始化
// 讀入值
for(int i = 0; i < k; ++i)
{
int value = atoi( AnsiString(om[i].src->Text).c_str() );
src.push_back( value );
}
///////////////////////////
bubble_sort(src, 0);
///////////////////////////
for(int i = 0; i < k; ++i)
{
om[i].dst->Text = src[i];
}
}
其實很多技巧都牽涉到底層的硬體運作模式,所以寫程式不能只會軟體知識,到後期要高效能要了解一些硬體的東西,或是底層的處理方式,
但是基本上大型的程式有時候為了維護方便,基本上有時候會使用偷懶的方式,就是假設要設定一些指令,假設設定處理時間不會太久,會將設定指令都寫在一起,也就是我要設定指令時,呼叫相同一個函式就可以了,不用呼叫不同函式
最後,其實上訴這些東西算是一個概念,而要做排序在C++是不會使用bubble sort,會用STL的sort
Bubble sort要花O(n)
Sort指令只要花(nlogn)
基本上Sort會比較快就是了,當排序的東西比較多的時候,怎麼知道呢,就是量一量程式片段的時間就知道了,這裡一樣也不做示範
Sort用法如下
#include <algorithm>
void __fastcall TForm2::BitBtn3Click(TObject *Sender)
{
vector<int> src;
struct Object_Mapping
{
TEdit* src;
TEdit* dst;
};
Object_Mapping om[] =
{
{Edit11, Edit21},
{Edit12, Edit22},
{Edit13, Edit23},
{Edit14, Edit24},
{Edit15, Edit25},
{Edit16, Edit26},
{Edit17, Edit27},
{Edit18, Edit28},
{Edit19, Edit29},
{Edit20, Edit30},
//{Edit31, Edit32},
};
int k = sizeof(om) / sizeof(Object_Mapping);
// 將整個陣列丟掉
vector<int>().swap(src);
// 預配置陣列大小
src.reserve(k);
// 由於每個值都會蓋過去,所以我們不對vector初始化
// 讀入值
for(int i = 0; i < k; ++i)
{
int value = atoi( AnsiString(om[i].src->Text).c_str() );
src.push_back( value );
}
///////////////////////////
//bubble_sort(src, 0);
sort(src.begin(), src.end() );
///////////////////////////
for(int i = 0; i < k; ++i)
{
om[i].dst->Text = src[i];
}
}
:D簡單又方便,很多東西其實在C++都已經有現成的了,所以不用自己再寫,而且那些效率都是很高的,其實我也只會到這邊,還有一些特別的我也不是很清楚就是了
短短的一個bubble sort,可以牽扯到那麼多,這些都是基本的,不會真的不太容易寫好程式
而C#就我淺淺的知道,他是比較簡單,但似乎他很多東西都沒有(例如好用的vector和STL),所以到底好不好用就只能問會用的人了
這裡是範例連結
https://drive.google.com/open?id=0B6u5oifJv3EKYmxLY05TcjVJbkU
沒有留言:
張貼留言