摘要: 之所以撰寫這篇文章是因?yàn)榍岸螘r(shí)間花費(fèi)了很大的精力在已經(jīng)成熟的代碼上再去處理memory leak問題。寫此的目的是希望我們應(yīng)該養(yǎng)成良好的編碼習(xí)慣,盡可能的避免這樣的問題,因?yàn)楫?dāng)你對著一大片的代碼再去處理此類的問題,此時(shí)無疑增加了解決的成本和難度。準(zhǔn)確的說屬于補(bǔ)救措施了。1. 什么是內(nèi)存泄漏(memory leak)? 指由于疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并非...
閱讀全文
posted @
2013-05-02 18:29 王海光 閱讀(7184) |
評論 (0) |
編輯 收藏
現(xiàn)在流行的進(jìn)程線程同步互斥的控制機(jī)制,其實(shí)是由最原始最基本的4種方法實(shí)現(xiàn)的。由這4種方法組合優(yōu)化就有了.Net和Java下靈活多變的,編程簡便的線程進(jìn)程控制手段。
這4種方法具體定義如下 在《操作系統(tǒng)教程》ISBN 7-5053-6193-7 一書中可以找到更加詳細(xì)的解釋
1、臨界區(qū):通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數(shù)據(jù)訪問。
2、互斥量:為協(xié)調(diào)共同對一個(gè)共享資源的單獨(dú)訪問而設(shè)計(jì)的。
3、信號量:為控制一個(gè)具有有限數(shù)量用戶資源而設(shè)計(jì)。
4、事 件:用來通知線程有一些事件已發(fā)生,從而啟動后繼任務(wù)的開始。
臨界區(qū)(Critical Section)
保證在某一時(shí)刻只有一個(gè)線程能訪問數(shù)據(jù)的簡便辦法。在任意時(shí)刻只允許一個(gè)線程對共享資源進(jìn)行訪問。如果有多個(gè)線程試圖同時(shí)訪問臨界區(qū),那么在有一個(gè)線程進(jìn)入后其他所有試圖訪問此臨界區(qū)的線程將被掛起,并一直持續(xù)到進(jìn)入臨界區(qū)的線程離開。臨界區(qū)在被釋放后,其他線程可以繼續(xù)搶占,并以此達(dá)到用原子方式操作共享資源的目的。
臨界區(qū)包含兩個(gè)操作原語:
EnterCriticalSection() 進(jìn)入臨界區(qū)
LeaveCriticalSection() 離開臨界區(qū)
EnterCriticalSection()語句執(zhí)行后代碼將進(jìn)入臨界區(qū)以后無論發(fā)生什么,必須確保與之匹配的LeaveCriticalSection()都能夠被執(zhí)行到。否則臨界區(qū)保護(hù)的共享資源將永遠(yuǎn)不會被釋放。雖然臨界區(qū)同步速度很快,但卻只能用來同步本進(jìn)程內(nèi)的線程,而不可用來同步多個(gè)進(jìn)程中的線程。
MFC提供了很多功能完備的類,我用MFC實(shí)現(xiàn)了臨界區(qū)。MFC為臨界區(qū)提供有一個(gè)CCriticalSection類,使用該類進(jìn)行線程同步處理是非常簡單的。只需在線程函數(shù)中用CCriticalSection類成員函數(shù)Lock()和UnLock()標(biāo)定出被保護(hù)代碼片段即可。Lock()后代碼用到的資源自動被視為臨界區(qū)內(nèi)的資源被保護(hù)。UnLock后別的線程才能訪問這些資源。
//CriticalSection
CCriticalSection global_CriticalSection;
// 共享資源
char global_Array[256];
//初始化共享資源
void InitializeArray()
{
for(int i = 0;i<256;i++)
{
global_Array[i]=I;
}
}
//寫線程
UINT Global_ThreadWrite(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
//進(jìn)入臨界區(qū)
global_CriticalSection.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=W;
ptr->SetWindowText(global_Array);
Sleep(10);
}
//離開臨界區(qū)
global_CriticalSection.Unlock();
return 0;
}
//刪除線程
UINT Global_ThreadDelete(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
//進(jìn)入臨界區(qū)
global_CriticalSection.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=D;
ptr->SetWindowText(global_Array);
Sleep(10);
}
//離開臨界區(qū)
global_CriticalSection.Unlock();
return 0;
}
//創(chuàng)建線程并啟動線程
void CCriticalSectionsDlg::OnBnClickedButtonLock()
{
//Start the first Thread
CWinThread *ptrWrite = AfxBeginThread(Global_ThreadWrite,
&m_Write,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrWrite->ResumeThread();
//Start the second Thread
CWinThread *ptrDelete = AfxBeginThread(Global_ThreadDelete,
&m_Delete,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrDelete->ResumeThread();
}
在測試程序中,Lock UnLock兩個(gè)按鈕分別實(shí)現(xiàn),在有臨界區(qū)保護(hù)共享資源的執(zhí)行狀態(tài),和沒有臨界區(qū)保護(hù)共享資源的執(zhí)行狀態(tài)。
互斥量(Mutex)
互斥量跟臨界區(qū)很相似,只有擁有互斥對象的線程才具有訪問資源的權(quán)限,由于互斥對象只有一個(gè),因此就決定了任何情況下此共享資源都不會同時(shí)被多個(gè)線程所訪問。當(dāng)前占據(jù)資源的線程在任務(wù)處理完后應(yīng)將擁有的互斥對象交出,以便其他線程在獲得后得以訪問資源。互斥量比臨界區(qū)復(fù)雜。因?yàn)槭褂没コ獠粌H僅能夠在同一應(yīng)用程序不同線程中實(shí)現(xiàn)資源的安全共享,而且可以在不同應(yīng)用程序的線程之間實(shí)現(xiàn)對資源的安全共享。
互斥量包含的幾個(gè)操作原語:
CreateMutex() 創(chuàng)建一個(gè)互斥量
OpenMutex() 打開一個(gè)互斥量
ReleaseMutex() 釋放互斥量
WaitForMultipleObjects() 等待互斥量對象
同樣MFC為互斥量提供有一個(gè)CMutex類。使用CMutex類實(shí)現(xiàn)互斥量操作非常簡單,但是要特別注意對CMutex的構(gòu)造函數(shù)的調(diào)用
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL)
不用的參數(shù)不能亂填,亂填會出現(xiàn)一些意想不到的運(yùn)行結(jié)果。
//創(chuàng)建互斥量
CMutex global_Mutex(0,0,0);
// 共享資源
char global_Array[256];
void InitializeArray()
{
for(int i = 0;i<256;i++)
{
global_Array[i]=I;
}
}
UINT Global_ThreadWrite(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
global_Mutex.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=W;
ptr->SetWindowText(global_Array);
Sleep(10);
}
global_Mutex.Unlock();
return 0;
}
UINT Global_ThreadDelete(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
global_Mutex.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=D;
ptr->SetWindowText(global_Array);
Sleep(10);
}
global_Mutex.Unlock();
return 0;
}
同樣在測試程序中,Lock UnLock兩個(gè)按鈕分別實(shí)現(xiàn),在有互斥量保護(hù)共享資源的執(zhí)行狀態(tài),和沒有互斥量保護(hù)共享資源的執(zhí)行狀態(tài)。
信號量(Semaphores)
信號量對象對線程的同步方式與前面幾種方法不同,信號允許多個(gè)線程同時(shí)使用共享資源,這與操作系統(tǒng)中的PV操作相同。它指出了同時(shí)訪問共享資源的線程最大數(shù)目。它允許多個(gè)線程在同一時(shí)刻訪問同一資源,但是需要限制在同一時(shí)刻訪問此資源的最大線程數(shù)目。在用CreateSemaphore()創(chuàng)建信號量時(shí)即要同時(shí)指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個(gè)線程對共享資源的訪問,當(dāng)前可用資源計(jì)數(shù)就會減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號量信號。但是當(dāng)前可用計(jì)數(shù)減小到0時(shí)則說明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能在允許其他線程的進(jìn)入,此時(shí)的信號量信號將無法發(fā)出。線程在處理完共享資源后,應(yīng)在離開的同時(shí)通過ReleaseSemaphore()函數(shù)將當(dāng)前可用資源計(jì)數(shù)加1。在任何時(shí)候當(dāng)前可用資源計(jì)數(shù)決不可能大于最大資源計(jì)數(shù)。
PV操作及信號量的概念都是由荷蘭科學(xué)家E.W.Dijkstra提出的。信號量S是一個(gè)整數(shù),S大于等于零時(shí)代表可供并發(fā)進(jìn)程使用的資源實(shí)體數(shù),但S小于零時(shí)則表示正在等待使用共享資源的進(jìn)程數(shù)。
P操作 申請資源:
(1)S減1;
(2)若S減1后仍大于等于零,則進(jìn)程繼續(xù)執(zhí)行;
(3)若S減1后小于零,則該進(jìn)程被阻塞后進(jìn)入與該信號相對應(yīng)的隊(duì)列中,然后轉(zhuǎn)入進(jìn)程調(diào)度。
V操作 釋放資源:
(1)S加1;
(2)若相加結(jié)果大于零,則進(jìn)程繼續(xù)執(zhí)行;
(3)若相加結(jié)果小于等于零,則從該信號的等待隊(duì)列中喚醒一個(gè)等待進(jìn)程,然后再返回原進(jìn)程繼續(xù)執(zhí)行或轉(zhuǎn)入進(jìn)程調(diào)度。
信號量包含的幾個(gè)操作原語:
CreateSemaphore() 創(chuàng)建一個(gè)信號量
OpenSemaphore() 打開一個(gè)信號量
ReleaseSemaphore() 釋放信號量
WaitForSingleObject() 等待信號量
//信號量句柄
HANDLE global_Semephore;
// 共享資源
char global_Array[256];
void InitializeArray()
{
for(int i = 0;i<256;i++)
{
global_Array[i]=I;
}
}
//線程1
UINT Global_ThreadOne(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
//等待對共享資源請求被通過 等于 P操作
WaitForSingleObject(global_Semephore, INFINITE);
for(int i = 0;i<256;i++)
{
global_Array[i]=O;
ptr->SetWindowText(global_Array);
Sleep(10);
}
//釋放共享資源 等于 V操作
ReleaseSemaphore(global_Semephore, 1, NULL);
return 0;
}
UINT Global_ThreadTwo(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
WaitForSingleObject(global_Semephore, INFINITE);
for(int i = 0;i<256;i++)
{
global_Array[i]=T;
ptr->SetWindowText(global_Array);
Sleep(10);
}
ReleaseSemaphore(global_Semephore, 1, NULL);
return 0;
}
UINT Global_ThreadThree(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
WaitForSingleObject(global_Semephore, INFINITE);
for(int i = 0;i<256;i++)
{
global_Array[i]=H;
ptr->SetWindowText(global_Array);
Sleep(10);
}
ReleaseSemaphore(global_Semephore, 1, NULL);
return 0;
}
void CSemaphoreDlg::OnBnClickedButtonOne()
{
//設(shè)置信號量 1 個(gè)資源 1同時(shí)只可以有一個(gè)線程訪問
global_Semephore= CreateSemaphore(NULL, 1, 1, NULL);
this->StartThread();
// TODO: Add your control notification handler code here
}
void CSemaphoreDlg::OnBnClickedButtonTwo()
{
//設(shè)置信號量 2 個(gè)資源 2 同時(shí)只可以有兩個(gè)線程訪問
global_Semephore= CreateSemaphore(NULL, 2, 2, NULL);
this->StartThread();
// TODO: Add your control notification handler code here
}
void CSemaphoreDlg::OnBnClickedButtonThree()
{
//設(shè)置信號量 3 個(gè)資源 3 同時(shí)只可以有三個(gè)線程訪問
global_Semephore= CreateSemaphore(NULL, 3, 3, NULL);
this->StartThread();
// TODO: Add your control notification handler code here
}
信號量的使用特點(diǎn)使其更適用于對Socket(套接字)程序中線程的同步。例如,網(wǎng)絡(luò)上的HTTP服務(wù)器要對同一時(shí)間內(nèi)訪問同一頁面的用戶數(shù)加以限制,這時(shí)可以為每一個(gè)用戶對服務(wù)器的頁面請求設(shè)置一個(gè)線程,而頁面則是待保護(hù)的共享資源,通過使用信號量對線程的同步作用可以確保在任一時(shí)刻無論有多少用戶對某一頁面進(jìn)行訪問,只有不大于設(shè)定的最大用戶數(shù)目的線程能夠進(jìn)行訪問,而其他的訪問企圖則被掛起,只有在有用戶退出對此頁面的訪問后才有可能進(jìn)入。
事件(Event)
事件對象也可以通過通知操作的方式來保持線程的同步。并且可以實(shí)現(xiàn)不同進(jìn)程中的線程同步操作。
信號量包含的幾個(gè)操作原語:
CreateEvent() 創(chuàng)建一個(gè)信號量
OpenEvent() 打開一個(gè)事件
SetEvent() 回置事件
WaitForSingleObject() 等待一個(gè)事件
WaitForMultipleObjects() 等待多個(gè)事件
WaitForMultipleObjects 函數(shù)原型:
WaitForMultipleObjects(
IN DWORD nCount, // 等待句柄數(shù)
IN CONST HANDLE *lpHandles, //指向句柄數(shù)組
IN BOOL bWaitAll, //是否完全等待標(biāo)志
IN DWORD dwMilliseconds //等待時(shí)間
)
參數(shù)nCount指定了要等待的內(nèi)核對象的數(shù)目,存放這些內(nèi)核對象的數(shù)組由lpHandles來指向。fWaitAll對指定的這nCount個(gè)內(nèi)核對象的兩種等待方式進(jìn)行了指定,為TRUE時(shí)當(dāng)所有對象都被通知時(shí)函數(shù)才會返回,為FALSE則只要其中任何一個(gè)得到通知就可以返回。dwMilliseconds在這里的作用與在WaitForSingleObject()中的作用是完全一致的。如果等待超時(shí),函數(shù)將返回WAIT_TIMEOUT。
//事件數(shù)組
HANDLE global_Events[2];
// 共享資源
char global_Array[256];
void InitializeArray()
{
for(int i = 0;i<256;i++)
{
global_Array[i]=I;
}
}
UINT Global_ThreadOne(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
for(int i = 0;i<256;i++)
{
global_Array[i]=O;
ptr->SetWindowText(global_Array);
Sleep(10);
}
//回置事件
SetEvent(global_Events[0]);
return 0;
}
UINT Global_ThreadTwo(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
for(int i = 0;i<256;i++)
{
global_Array[i]=T;
ptr->SetWindowText(global_Array);
Sleep(10);
}
//回置事件
SetEvent(global_Events[1]);
return 0;
}
UINT Global_ThreadThree(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
//等待兩個(gè)事件都被回置
WaitForMultipleObjects(2, global_Events, true, INFINITE);
for(int i = 0;i<256;i++)
{
global_Array[i]=H;
ptr->SetWindowText(global_Array);
Sleep(10);
}
return 0;
}
void CEventDlg::OnBnClickedButtonStart()
{
for (int i = 0; i < 2; i++)
{
//實(shí)例化事件
global_Events[i]=CreateEvent(NULL,false,false,NULL);
}
CWinThread *ptrOne = AfxBeginThread(Global_ThreadOne,
&m_One,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrOne->ResumeThread();
//Start the second Thread
CWinThread *ptrTwo = AfxBeginThread(Global_ThreadTwo,
&m_Two,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrTwo->ResumeThread();
//Start the Third Thread
CWinThread *ptrThree = AfxBeginThread(Global_ThreadThree,
&m_Three,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrThree->ResumeThread();
// TODO: Add your control notification handler code here
}
事件可以實(shí)現(xiàn)不同進(jìn)程中的線程同步操作,并且可以方便的實(shí)現(xiàn)多個(gè)線程的優(yōu)先比較等待操作,例如寫多個(gè)WaitForSingleObject來代替WaitForMultipleObjects從而使編程更加靈活。
總結(jié):
1. 互斥量與臨界區(qū)的作用非常相似,但互斥量是可以命名的,也就是說它可以跨越進(jìn)程使用。所以創(chuàng)建互斥量需要的資源更多,所以如果只為了在進(jìn)程內(nèi)部是用的話使用臨界區(qū)會帶來速度上的優(yōu)勢并能夠減少資源占用量。因?yàn)榛コ饬渴强邕M(jìn)程的互斥量一旦被創(chuàng)建,就可以通過名字打開它。
2. 互斥量(Mutex),信號燈(Semaphore),事件(Event)都可以被跨越進(jìn)程使用來進(jìn)行同步數(shù)據(jù)操作,而其他的對象與數(shù)據(jù)同步操作無關(guān),但對于進(jìn)程和線程來講,如果進(jìn)程和線程在運(yùn)行狀態(tài)則為無信號狀態(tài),在退出后為有信號狀態(tài)。所以可以使用WaitForSingleObject來等待進(jìn)程和線程退出。
3. 通過互斥量可以指定資源被獨(dú)占的方式使用,但如果有下面一種情況通過互斥量就無法處理,比如現(xiàn)在一位用戶購買了一份三個(gè)并發(fā)訪問許可的數(shù)據(jù)庫系統(tǒng),可以根據(jù)用戶購買的訪問許可數(shù)量來決定有多少個(gè)線程/進(jìn)程能同時(shí)進(jìn)行數(shù)據(jù)庫操作,這時(shí)候如果利用互斥量就沒有辦法完成這個(gè)要求,信號燈對象可以說是一種資源計(jì)數(shù)器。
疑問:
在 Linux 上,有兩類信號量。第一類是由 semget/semop/semctl API 定義的信號量的 SVR4(System V Release 4)版本。第二類是由 sem_init/sem_wait/sem_post/interfaces 定義的 POSIX 接口。 它們具有相同的功能,但接口不同。 在2.4.x內(nèi)核中,信號量數(shù)據(jù)結(jié)構(gòu)定義為(include/asm/semaphore.h)。
但是在Linux中沒有對互斥量的具體提法,只是看到說互斥量是信號量的一種特殊情況,當(dāng)信號量的最大資源數(shù)=1同時(shí)可以訪問共享資源的線程數(shù)=1 就是互斥量了。臨界區(qū)的定義也比較模糊。沒有找到用事件處理線程/進(jìn)程同步互斥的操作的相關(guān)資料。在Linux下用GCC/G++編譯標(biāo)準(zhǔn)C++代碼,信號量的操作幾乎和Windows下VC7的編程一樣,不用改多少就順利移植了,可是互斥量,事件,臨界區(qū)的Linux移植沒有成功。
本文所有事例程序在WindowsXp Sp2 + VC7 下編譯通過。
本文轉(zhuǎn)自:http://www.bccn.net/Article/kfyy/vc/jszl/200709/6380_2.html
posted @
2013-04-18 17:06 王海光 閱讀(514) |
評論 (0) |
編輯 收藏
解釋以下語句的含義:
1、new A;
2、new A();
也許很多人包括我自己,都可以馬上給出第一種情況的答案:在堆上為A類分配內(nèi)存,然后調(diào)用A的構(gòu)造函數(shù)。這種說法被大家所熟知,因?yàn)榘ā?/span>STL源碼剖析》等大作在內(nèi)也都是這么寫的(但是你認(rèn)為這種說法完全正確嗎?其實(shí)不盡然,答案后面揭曉)
第二種情況,對象構(gòu)造的時(shí)候初始化列表為空會和第一種有什么不同呢?對于這種在實(shí)際工程中很少使用的情況,我一時(shí)還真給不出確切的答案。
網(wǎng)上搜了一下,看到CSDN里面還有專門針對這個(gè)問題的一個(gè)帖子(原帖鏈接 http://bbs.csdn.net/topics/320161716)。
好像最終也沒有可以信服的答案,認(rèn)同度比較高的是這樣的說法:“加括號調(diào)用沒有參數(shù)的構(gòu)造函數(shù),不加括號調(diào)用默認(rèn)構(gòu)造函數(shù)或唯一的構(gòu)造函數(shù),看需求” (peakflys注:這種說法是錯誤的,答案后面揭曉)
既然沒有特別靠譜的答案,不如自己動手找出答案。
構(gòu)造以下示例:
/**
*\brief example1 difference between new and new()
*\author peakflys
*\data 12:10:24 Monday, April 08, 2013
*/
class A
{
public:
int a;
};
int main()
{
A *pa = new A;
A *paa = new A();
return 0;
}
查看main函數(shù)的匯編代碼(編譯器:gcc (GCC) 4.4.6 20120305 (Red Hat
4.4.6-4) )
int main()
{
4005c4: 55 push %rbp
4005c5: 48 89 e5 mov %rsp,%rbp
4005c8: 48 83 ec 10 sub $0x10,%rsp
A *pa = new A;
4005cc: bf 04 00 00 00 mov $0x4,%edi
4005d1: e8 f2 fe ff ff callq 4004c8 <_Znwm@plt> //調(diào)用new
4005d6: 48 89 45 f0 mov %rax,-0x10(%rbp) //rax寄存器內(nèi)容賦給指針pa(rax寄存器里是new調(diào)用產(chǎn)生的A對象堆內(nèi)存地址)
A *paa = new A();
4005da: bf 04 00 00 00 mov $0x4,%edi
4005df: e8 e4 fe ff ff callq 4004c8 <_Znwm@plt> //調(diào)用new
4005e4: 48 89 c2 mov %rax,%rdx //rax的內(nèi)容放入rdx,執(zhí)行之后,rdx里存放的即是通過new A()產(chǎn)生的內(nèi)存地址
4005e7: c7 02 00 00 00 00 movl $0x0,(%rdx) //把rdx內(nèi)存指向的內(nèi)容賦為0值,即把A::a賦值為0
4005ed: 48 89 45 f8 mov %rax,-0x8(%rbp) //rax寄存器內(nèi)容賦給指針paa(rax寄存器里是new()調(diào)用產(chǎn)生的A對象堆內(nèi)存地址)
return 0;
4005f1: b8 00 00 00 00 mov $0x0,%eax
}
4005f6: c9 leaveq
4005f7: c3 retq
通過上面產(chǎn)生的匯編代碼(對AT&T匯編不熟悉的可以看注釋)可以很容易看出,new A()的執(zhí)行,在調(diào)用完operator new分配內(nèi)存后,馬上對新分配內(nèi)存中的對象使用0值初始化,而new A 僅僅是調(diào)用了operator new分配內(nèi)存!
是不是這樣就可以下結(jié)論 new A()比new A多了一步,即初始化對象的步驟呢?
我們再看看下面這種情況:/**
*\brief example2 difference between new and new()
*\author peakflys
*\data 12:23:20 Monday, April 08, 2013
*/
class A
{
public:
A(){a = 10;}
int a;
};
int main()
{
A *pa = new A;
A *paa = new A();
return 0;
}
這種情況是類顯示提供含默認(rèn)值的構(gòu)造函數(shù)。
查看匯編實(shí)現(xiàn)如下:
int main()
{
4005c4: 55 push %rbp
4005c5: 48 89 e5 mov %rsp,%rbp
4005c8: 53 push %rbx
4005c9: 48 83 ec 18 sub $0x18,%rsp
A *pa = new A;
4005cd: bf 04 00 00 00 mov $0x4,%edi
4005d2: e8 f1 fe ff ff callq 4004c8 <_Znwm@plt>
4005d7: 48 89 c3 mov %rax,%rbx
4005da: 48 89 d8 mov %rbx,%rax
4005dd: 48 89 c7 mov %rax,%rdi
4005e0: e8 2d 00 00 00 callq 400612 <_ZN1AC1Ev>
4005e5: 48 89 5d e0 mov %rbx,-0x20(%rbp)
A *paa = new A();
4005e9: bf 04 00 00 00 mov $0x4,%edi
4005ee: e8 d5 fe ff ff callq 4004c8 <_Znwm@plt>
4005f3: 48 89 c3 mov %rax,%rbx
4005f6: 48 89 d8 mov %rbx,%rax
4005f9: 48 89 c7 mov %rax,%rdi
4005fc: e8 11 00 00 00 callq 400612 <_ZN1AC1Ev>
400601: 48 89 5d e8 mov %rbx,-0x18(%rbp)
return 0;
400605: b8 00 00 00 00 mov $0x0,%eax
}
40060a: 48 83 c4 18 add $0x18,%rsp
40060e: 5b pop %rbx
40060f: c9 leaveq
400610: c3 retq
上面的匯編代碼就不在添加注釋了,因?yàn)閮煞N操作產(chǎn)生的匯編代碼是一樣的,都是先調(diào)用operator new分配內(nèi)存,然后調(diào)用構(gòu)造函數(shù)。
上面的情況在VS2010下驗(yàn)證是一樣的情況,有興趣的朋友可以自己去看,這里就不再貼出VS2010下的匯編代碼了。
通過上面的分析,對于new A和 new A() 的區(qū)別,我們可以得出下面的結(jié)論:
1、類體含有顯示適合地默認(rèn)構(gòu)造函數(shù)時(shí),new A和new A()的作用一致,都是首先調(diào)用operator new分配內(nèi)存,然后調(diào)用默認(rèn)構(gòu)造函數(shù)初始化對象。
2、類體無顯示構(gòu)造函數(shù)時(shí),new A()首先調(diào)用operator new來為對象分配內(nèi)存,然后使用空值初始化對象成員變量,而new A僅僅是調(diào)用operator new分配內(nèi)存,對象的成員變量是無意義的隨機(jī)值! (peakflys注:對于基本數(shù)據(jù)類型,如int等 適用此條)
注意到,現(xiàn)在很多書籍對new操作符的說明都存在紕漏,例如《STL源碼剖析》中2.2.2節(jié)中有以下的描述:

事實(shí)證明,new Foo的操作是否有構(gòu)造函數(shù)的調(diào)用是不確定的,具體要看Foo類體里是否有顯示構(gòu)造函數(shù)的出現(xiàn)。
/*****************************************華麗分割線**************************************
補(bǔ)充:剛才發(fā)現(xiàn),在C++Primer第四版5.11節(jié)中,已經(jīng)有了對于new A()的說明:
We indicate that we want to value-initialize the newly allocated object by following the type nameby a pair of empty parentheses. The empty parentheses signal that we want initialization but arenot supplying a specific initial value. In the case of class types (such as string) that define their own constructors, requesting value-initialization is of no consequence: The object is initialized by running the default constructor whether we leave it apparently uninitialized orask for value-initialization. In the case of built-in types or types that do not define any constructors, the difference is significant:
int *pi = new int; // pi points to an uninitialized int
int *pi = new int(); // pi points to an int value-initialized to 0
In the first case, the int is uninitialized; in the second case, the int is initialized to zero.
這里給出的解釋和上面自己分析的new A()的行為是一致的。
/***************************************再次華麗分割線************************************
鑒于上面的結(jié)論是通過GCC和VS2010得出的,而且有朋友也提出同樣的質(zhì)疑,為了確定這種結(jié)果是否是編譯器相關(guān)的,剛才特意查看了一下C++的標(biāo)準(zhǔn)化文檔。
摘自:ISO/IEC 14882:2003(E) 5.3.4 - 15
— If the new-initializer is omitted:
— If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is default-initialized(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared default constructor.
— Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;
— If the new-initializer is of the form (), the item is value-initialized (8.5);
所以可以確定,這種情況完全是編譯器無關(guān)的(當(dāng)然那些不完全按照標(biāo)準(zhǔn)實(shí)現(xiàn)的編譯器除外)。
但是通過上面標(biāo)準(zhǔn)化文檔的描述,我們可以看出文中對new A在無顯示構(gòu)造函數(shù)時(shí)的總結(jié)并不是特別準(zhǔn)確,鑒于很多公司都有這道面試題(撇去這些題目的實(shí)際考察意義不說),我們有必要再補(bǔ)充一下: 對于new A: 這樣的語句,再調(diào)用完operator new分配內(nèi)存之后,如果A類體內(nèi)含有POD類型,則POD類型的成員變量處于未定義狀態(tài),如果含有非POD類型則調(diào)用該類型的默認(rèn)構(gòu)造函數(shù)。而 new A()在這些情況下都會初始化。
PS:估計(jì)很多公司的“正確答案“ 也不一定正確吧。
本文轉(zhuǎn)自:http://www.shnenglu.com/peakflys/archive/2013/04/08/199208.html
posted @
2013-04-18 11:03 王海光 閱讀(485) |
評論 (0) |
編輯 收藏
來源于《敏捷軟件開發(fā)——原則、模式與實(shí)踐》常見的設(shè)計(jì)的臭味——腐化軟件的氣味。l 僵化性(Rigidity):很難對系統(tǒng)進(jìn)行改動,因?yàn)槊總€(gè)改動都會迫使許多對系統(tǒng)其他部分的其他改動。
l 脆弱性(Fragility):對系統(tǒng)的改動會導(dǎo)致系統(tǒng)中和改動的地方在概念上無關(guān)的許多地方出現(xiàn)問題。
l 牢固性(Immobility):很難解開系統(tǒng)的糾結(jié),使之成為一些可在其他系統(tǒng)中重用的組件。
l 粘滯性(Viscosity):做正確的事情比做錯誤的事情要困難。
l 不必要的復(fù)雜性(Needless Complexity):設(shè)計(jì)中包含有不具任何直接好處的基礎(chǔ)結(jié)構(gòu)。
l 不必要的重復(fù)(Needless Repetition):設(shè)計(jì)中包含有重復(fù)的結(jié)構(gòu),而該重復(fù)的結(jié)構(gòu)本可以使用單一的抽象進(jìn)行統(tǒng)一。
l 晦澀性(Opacity):很難閱讀、理解。沒有很好的表現(xiàn)出意圖。
敏捷設(shè)計(jì)是一個(gè)過程,不是一個(gè)事件。它是一個(gè)持續(xù)的應(yīng)用原則、模式以及實(shí)踐來改進(jìn)軟件的結(jié)構(gòu)和可讀性的過程。它致力于保持系統(tǒng)設(shè)計(jì)在任何時(shí)間都盡可能得簡單、干凈以及富有表現(xiàn)力。
敏捷軟件開發(fā)宣言:
我們正在通過親身實(shí)踐以及幫助他人實(shí)踐,揭示更好的軟件開發(fā)方法。通過這項(xiàng)工作,我們認(rèn)為:
個(gè)體和交互 勝過 過程和工具
可以工作的軟件 勝過 面面俱到的文檔
客戶合作 勝過 合同談判
響應(yīng)變化 勝過 遵循計(jì)劃
雖然右項(xiàng)也具有價(jià)值,但我們認(rèn)為左項(xiàng)具有更大的價(jià)值。
敏捷開發(fā)強(qiáng)調(diào)以人為中心,而不是以過程為中心,強(qiáng)調(diào)盡可能的溝通(與客戶,與團(tuán)隊(duì)成員),盡可能地以最簡單的設(shè)計(jì)解決問題(從而能夠擁抱變化)。
敏捷宣言遵循的原則
我們遵循以下原則:
1。我們最優(yōu)先要做的是通過盡早的、持續(xù)的交付有價(jià)值的軟件來使客戶滿意。
規(guī)劃迭代故事時(shí)必須按照優(yōu)先級安排,為客戶先提供最有價(jià)值的功能。通過頻繁迭代能與客戶形成早期的良好合作,及時(shí)反饋提高產(chǎn)品質(zhì)量。敏捷小組關(guān)注完成和交 付具有用戶價(jià)值的功能,而不是孤立的任務(wù)。以前我們都用需求規(guī)格說明書或者用例來編寫詳細(xì)的需求,敏捷使用用戶故事來羅列需求。用戶故事是一種表示需求的 輕量級技術(shù),它沒有
固定的形式和強(qiáng)制性的語法。但是有一些固定的形式可以用來參考還是比較有益的。敏捷估算中使用了這個(gè)模板:“作為【用戶的類型】,我希 望可以【能力】以便【業(yè)務(wù)價(jià)值】“。使用基于用戶故事的需求分析方法時(shí),仍可能需要原型和編寫文檔,只是工作重點(diǎn)更多的轉(zhuǎn)移到了口頭交流。
2。即使到了開發(fā)的后期,也歡迎改變需求。敏捷過程利用變化來為客戶創(chuàng)造競爭優(yōu)勢。
敏捷過程參與者不怕變化,他們認(rèn)為改變需求是好事情,因?yàn)檫@些改變意味著我們更了解市場需求。
3。經(jīng)常性的交付可以工作的軟件,交付的間隔可以從幾周到幾個(gè)月,交付的時(shí)間間隔越短越好。
迭代是受實(shí)踐框限制的,意味著即使放棄一些功能也必須按時(shí)結(jié)束迭代。只要我們可以保證交付的軟件可以很好的工作,那么交付時(shí)間越短,我們和客戶協(xié)作就越 緊密,對產(chǎn)品質(zhì)量就更有益。雖然我們多次迭代,但并不是每次迭代的結(jié)果都需要交付給用戶,敏捷開發(fā)的目標(biāo)是讓他們可以交付。這意味著開發(fā)小組在每次迭代中 都會增加一些功能,增加的每個(gè)功能都是經(jīng)過編碼、測試,達(dá)到了可發(fā)布的質(zhì)量標(biāo)準(zhǔn)的。
另外敏捷開發(fā)項(xiàng)目中對開發(fā)階段沒有什么重要的分割,沒有先期的需求階段,然后是分析階段,架構(gòu)設(shè)計(jì)階段,編碼測試階段等,在項(xiàng)目真正開始后,每次迭代中都會同時(shí)進(jìn)
行所有的上述階段工作。
4。在整個(gè)項(xiàng)目開發(fā)期間,業(yè)務(wù)人員和開發(fā)人員必須天天都在一起工作。
軟件項(xiàng)目不會依照之前設(shè)定的計(jì)劃原路執(zhí)行,中間對業(yè)務(wù)的理解、軟件的解決方案肯定會存在偏差,所以客戶、需求人員、開發(fā)人員以及涉眾之間必須進(jìn)行有意義的、頻繁
的交互,這樣就可以在早期及時(shí)的發(fā)現(xiàn)并解決問題。
5。圍繞被激勵起來的人個(gè)來構(gòu)建項(xiàng)目。給他們提供所需要的環(huán)境和支持,并且信任他們能夠完成工作。
業(yè)務(wù)和技術(shù)是引起不確定的二個(gè)主要方面,人是第三個(gè)方面。而業(yè)務(wù)和技術(shù)又必須由人來執(zhí)行,所以能夠激勵人來解決這些問題是解決不確定性的關(guān)鍵。只要個(gè)人的目標(biāo)和團(tuán)
隊(duì)的目標(biāo)一致,我們就需要鼓舞起每個(gè)人的積極性,以個(gè)人為中心構(gòu)建項(xiàng)目,提供所需的環(huán)境、支持與信任。
6。在團(tuán)隊(duì)內(nèi)部,最具有效果并且富有效率的傳遞信息的方法,就是面對面的交談。
在十幾或者二十幾個(gè)人組成的大團(tuán)隊(duì)中,文檔是一種比較合適的傳遞知識和交流的途徑。而敏捷團(tuán)隊(duì)一般不會很多人(大團(tuán)隊(duì)實(shí)施敏捷時(shí)也會分成多個(gè)小的敏捷團(tuán)隊(duì)),所以
大量的文檔交流其實(shí)并不是很經(jīng)濟(jì)的做法。此時(shí)面對面的交談反而更快速有效。
7、可工作的軟件是首要進(jìn)度度量標(biāo)準(zhǔn)。
一般的工作都比較容易衡量任務(wù)進(jìn)展,比如讓你去搬運(yùn)1噸的石頭,我只要去稱一下你已經(jīng)搬運(yùn)的石頭重量就知道你完成多少了。而對于軟件來說,在軟件沒有編 碼、測試完
成之前,我們都不能因?yàn)榇a編寫了多少行,測試用例跑了多少個(gè)就去度量這個(gè)功能是否完成了。衡量這個(gè)功能是否完成的首要標(biāo)準(zhǔn)就是這個(gè)功能可以工 作了,對用戶來說已經(jīng)可
以應(yīng)用了。
8。敏捷過程提可持續(xù)的開發(fā)速度。責(zé)任人、開發(fā)者和用戶應(yīng)該能夠保持一個(gè)長期的、恒定的開發(fā)速度。
很多人都認(rèn)為軟件開發(fā)中加班是很正常的,不加班反而不正常,我對此有點(diǎn)不理解,這個(gè)可能是國情所致吧。敏捷過程希望能夠可持續(xù)的進(jìn)行開發(fā),開發(fā)速度不會 隨著迭代的任務(wù)不同而不同,不欣賞所謂的拼一拼也能完成的態(tài)度,開發(fā)工作不應(yīng)該是突擊行為。我們不能指望說突擊這個(gè)項(xiàng)目后就可以輕松了,因?yàn)橥瓿梢粋€(gè)項(xiàng)目 后會接踵而來下一個(gè)項(xiàng)目,而只要還是拼拼的態(tài)度,下一個(gè)項(xiàng)目依舊會讓你的組員再次突擊。這時(shí)不知道有人會不會說,那我們就一直加班,也是“持續(xù)的開發(fā)速 度”啊,這時(shí)可要注意了,持續(xù)加班智
慧導(dǎo)致人疲勞、厭倦,保持長期恒定的速度也只是一種理想而已。
9。不斷地關(guān)注優(yōu)秀的技能和好的設(shè)計(jì)會增強(qiáng)敏捷能力。
敏捷過程有很多好的技術(shù)實(shí)踐可以加強(qiáng)產(chǎn)品敏捷能力,很多原則、模式和實(shí)踐也可以增強(qiáng)敏捷開發(fā)能力。 《敏捷軟件開發(fā)-原則、模式與實(shí)踐》一書中介紹了很多設(shè)計(jì),感興趣的可以去仔細(xì)看看。
10。簡單----使未完成的工作最大化的藝術(shù)----是根本的。
我們不可能預(yù)期后面需求會如何變化,所以不可能一開始就構(gòu)建一個(gè)完美的架構(gòu)來適應(yīng)以后的所有變化。敏捷團(tuán)隊(duì)不會去構(gòu)建明天的軟件,而把注意力放在如何通 過最簡單的方法完成現(xiàn)在需要解決的問題。這時(shí)有人會說,我已經(jīng)預(yù)計(jì)到了肯定存在哪些需求擴(kuò)展點(diǎn),我們在一開始是否需要考慮呢?這時(shí)團(tuán)隊(duì)需要根據(jù)自己的理解 去決定是否考慮,如果深信在明天發(fā)生了這個(gè)問題也可以輕易處理的話,那么就最好先不考慮。
11。最好的構(gòu)架、需求和設(shè)計(jì)出自與自組織的團(tuán)隊(duì)。
敏捷中有很多種實(shí)踐,大家都知道,迭代式開發(fā)是主要的實(shí)踐方法,而自組織團(tuán)隊(duì)也是主要的實(shí)踐之一。在自組織團(tuán)隊(duì)中,管理者不再發(fā)號施令,而是讓團(tuán)隊(duì)自身尋找最佳的工作方式來完成工作。要形成一個(gè)自組織團(tuán)隊(duì)其實(shí)比較難。CSDN采訪Mishkin Berteig中說到 自組織團(tuán)隊(duì)的第一個(gè)要素就是必須有一個(gè)團(tuán)隊(duì),而不僅僅是一群人。一群人是一幫在一起工作的人,他們彼此之間并沒有太多的溝通,他們也并不視彼此為一體。項(xiàng)目一開始,我們就會組建“團(tuán)隊(duì)”,但很多時(shí)候由構(gòu)架師、需求人員、開發(fā)人員和測試人員組成的是一群人而已。他還認(rèn)為,團(tuán)隊(duì)的形成必須經(jīng)歷幾個(gè)時(shí)期。在 經(jīng)歷了初期的磨合后,成員才會開始對團(tuán)隊(duì)共同的工作理念與文化形成一個(gè)基本的認(rèn)識和理解。團(tuán)隊(duì)內(nèi)會逐漸形成規(guī)矩,而且這些規(guī)矩是不言而喻的。比如,每個(gè)人 都知道上午九點(diǎn)來上班,都會主動詢問別人是否需要幫助,也都會去主動和別人探討問題。如果團(tuán)隊(duì)成員之間能夠達(dá)成這樣的默契,那么這個(gè)團(tuán)隊(duì)將成為一個(gè)真正高 效的工作團(tuán)隊(duì)。在這樣團(tuán)隊(duì)中,成員之間相互理解,工作效率非常高。在自組織團(tuán)隊(duì)中,團(tuán)隊(duì)成員不需要遵從別人的詳細(xì)指令。他們需要更高層次的指導(dǎo),這種指 導(dǎo)更像是一個(gè)目標(biāo),一個(gè)致力于開發(fā)出更好的軟件的目標(biāo)。總之,自組織團(tuán)隊(duì)是一個(gè)自動自發(fā)、有著共同目標(biāo)和工作文化的團(tuán)隊(duì),這樣的團(tuán)隊(duì)總是在向它的組織做出 承諾。但是,實(shí)現(xiàn)這些承諾對于自組織團(tuán)隊(duì)來說非常重要。否則,一旦出現(xiàn)問題,團(tuán)隊(duì)成員之間就會出現(xiàn)信任危機(jī)。
雖然敏捷開發(fā)小組是以小組為整體 來工作的,但是還是有必要指明一些承擔(dān)一定任務(wù)的角色。第一個(gè)角色是產(chǎn)品所有者(Product Owner)。產(chǎn)品所有者的主要職責(zé)包括:確認(rèn)小組所有成員都在追求一個(gè)共同的項(xiàng)目前景,確定功能的優(yōu)先級以便總是在處理最具有價(jià)值的功能,以及作出決定 使得對項(xiàng)目的投入可以產(chǎn)生良好的回報(bào)。可以對應(yīng)為以前開發(fā)中的“產(chǎn)品經(jīng)理”。另一角色是開發(fā)團(tuán)隊(duì)(developer),這里的開發(fā)人員包括了架構(gòu)師、設(shè)計(jì)師、程序員、需求人員、測試人員、文檔編寫者等,有時(shí)產(chǎn)品所有者也可以被看作是
開發(fā)人員。還有一個(gè)重要角色就是項(xiàng)目經(jīng)理(project manager)。敏捷開發(fā)的項(xiàng)目經(jīng)理會更多的關(guān)注領(lǐng)導(dǎo)而不是管理。在某些項(xiàng)目中,項(xiàng)目經(jīng)理可能同時(shí)也是開發(fā)人員,少數(shù)時(shí)候也會擔(dān)任產(chǎn)品所有者。
12。每隔一定時(shí)間,團(tuán)隊(duì)會在如何才能更有效地工作方面進(jìn)行反省,然后相應(yīng)地對自己的行為進(jìn)行調(diào)整。
由于很多不確定性因素會導(dǎo)致計(jì)劃失效,比如項(xiàng)目成員增減、技術(shù)應(yīng)用效果、用戶需求的改變、競爭者對我們的影響等都會讓我們作出不同的反應(yīng)。 敏捷不是基于預(yù)定義的工作方式,而是基于經(jīng)驗(yàn)性的方式,對以上這些變化,小組通過不斷的反省調(diào)整來保持團(tuán)隊(duì)的敏捷性。
面向?qū)ο笤O(shè)計(jì)的原則:
SRP 單一職責(zé)原則
就一個(gè)類而言,應(yīng)該僅有一個(gè)引起它變化的原因。
l 單一職責(zé)原則(The Single Responsibility Principle,簡稱SRP):就一個(gè)類而言,應(yīng)該僅有一個(gè)引起它變化的原因。在SRP中,我們把職責(zé)定義為“變化的原因()”。如果你能夠想到多于一個(gè)的動機(jī)去改變一個(gè)類,那么這個(gè)類就具有多于一個(gè)的職責(zé)。軟件設(shè)計(jì)真正要做的許多內(nèi)容,就是發(fā)現(xiàn)職責(zé)并把那些職責(zé)相互分離。事實(shí)上,我們將要論述的其余原則都會以這樣或那樣的方式回到這個(gè)問題上。
l 開放封閉原則(The Open-Close Principle,簡稱OCP):軟件實(shí)體(類、模塊、函數(shù)等等)應(yīng)該是可以擴(kuò)展的,但是不可以修改的。遵循開放封閉原則設(shè)計(jì)出的模塊具有兩個(gè)主要的特征。它們是:(1)、對于擴(kuò)展是開放的。這意味著模塊的行為是可以擴(kuò)展的。當(dāng)應(yīng)用的需求改變時(shí),我們可以對模塊進(jìn)行擴(kuò)展,使其具有滿足那些改變的新行為。換句話說,我們可以改變模塊的功能。(2)、對模塊行為進(jìn)行擴(kuò)展時(shí),不必改動模塊的源代碼或者二進(jìn)制代碼。模塊的二進(jìn)制可執(zhí)行版本,無論是可鏈接的庫、DLL或者Java的.jar文件,都無需改動。
l Liskov替換原則(The Liskov Substitution Principle,簡稱LSP):子類型必須能夠替換掉它們的基類型。OCP原則是OOD中很多說法的核心。LSP是使OCP成為可能的主要原則之一。正式子類型的可替換性才使得使用基類類型的模塊在無需修改的情況下就可以擴(kuò)展。這種可替換性必須是開發(fā)人員可以隱式依賴的東西。
l 依賴倒置原則(The Dependency Inversion Principle,簡稱DIP):(1)、高層模塊不應(yīng)該依賴于底層模塊。二者都應(yīng)該依賴于抽象。(2)、抽象不應(yīng)該依賴于細(xì)節(jié)。細(xì)節(jié)應(yīng)該依賴于抽象。使用傳統(tǒng)的過程化設(shè)計(jì)所創(chuàng)建出來的依賴關(guān)系結(jié)構(gòu),策略是依賴于細(xì)節(jié)的。面向?qū)ο蟮某绦蛟O(shè)計(jì)倒置了依賴關(guān)系結(jié)構(gòu),使得細(xì)節(jié)和策略都依賴于抽象,并且常常是客戶擁有服務(wù)接口。事實(shí)上,這種依賴關(guān)系正式好的面向?qū)ο笤O(shè)計(jì)的標(biāo)志所在。
l 接口隔離原則(The Interface Segregation Interface,簡稱ISP):不應(yīng)該強(qiáng)迫客戶依賴它們不用的方法。如果強(qiáng)迫客戶程序依賴于那些它們不適用的方法,那么這些客戶程序就面臨著由于這些未使用方法的改變所帶來的變更。這就無意中導(dǎo)致了所有客戶程序之間的耦合。我們希望盡可能地避免這種耦合,因此我們希望分離接口。
REP 重用發(fā)布等價(jià)原則
重用的粒度就是發(fā)布的粒度
CCP 共用封閉原則
包中的所有類對于同一類性質(zhì)的變化應(yīng)該是共同封閉的。一個(gè)變化若對一個(gè)包產(chǎn)生影響,則將對該包中的所有類產(chǎn)生影響,而對于其他的包不造成任何影響。
CRP 共同重用原則
一個(gè)包中的所有類應(yīng)該是共同重用的。如果重用了包中的一個(gè)類,那么就要重用包中所有類。
ADP 無環(huán)依賴原則
在包的依賴關(guān)系圖中不允許存在環(huán)。
SDP 穩(wěn)定依賴原則
朝著穩(wěn)定的方向進(jìn)行依賴。
SAP 穩(wěn)定抽象原則
包的抽象程度應(yīng)該和其穩(wěn)定程度一致。
極限編程實(shí)踐
完整團(tuán)隊(duì)
XP項(xiàng)目的所有參與者(開發(fā)人員、業(yè)務(wù)分析師、測試人員等等)一起工作在一個(gè)開放的場所中,他們是同一個(gè)團(tuán)隊(duì)的成員。
計(jì)劃游戲
計(jì)劃是持續(xù)的,循序漸進(jìn)的。每2周,開發(fā)人員就為下2周估算候選特性的成本,而客戶則根據(jù)成本和商務(wù)價(jià)值來選擇要實(shí)現(xiàn)的特性。
客戶測試
作為選擇每個(gè)所期望的特性的一部分,客戶定義出自動驗(yàn)收測試來表明該特性可以工作。
簡單設(shè)計(jì)
團(tuán)隊(duì)保持設(shè)計(jì)恰好和當(dāng)前的系統(tǒng)功能相匹配,它通過了所有的測試,不包含任何重復(fù),表達(dá)出了編寫者想表達(dá)的所有東西,并且包含盡可能少的代碼。
結(jié)對編程
所有的產(chǎn)品軟件都是由兩個(gè)程序員,并排坐在一起在同一臺電腦上構(gòu)建的。
測試驅(qū)動開發(fā)
程序員以非常短的循環(huán)周期工作,他們先增加一個(gè)失敗的測試,然后使之通過。
改進(jìn)設(shè)計(jì)
隨時(shí)改進(jìn)糟糕的代碼。保持代碼盡可能的干凈,具有表達(dá)力。
持續(xù)集成
團(tuán)隊(duì)總是使系統(tǒng)完整地被集成。
集體代碼所有權(quán)
任何結(jié)對的程序員都可以在任何時(shí)候改進(jìn)任何代碼。
編碼標(biāo)準(zhǔn)
系統(tǒng)中所有的代碼看起來就好像是被單獨(dú)一個(gè)--非常值得勝任的--人編寫的。
隱喻
團(tuán)隊(duì)提出一個(gè)程序工作原理的公共景像。
可持續(xù)的速度
團(tuán)隊(duì)只有持久才有獲勝的希望,他們以能夠長期維持的速度努力工作,他們保存精力,他們把項(xiàng)目看作是馬拉松長袍,而不是全速短跑。
測試驅(qū)動開發(fā)
極限編程(eXtreme Programming,簡稱XP)是敏捷方法中最著名的一個(gè)。它由一系列簡單卻相互依賴的時(shí)間組成。這些實(shí)踐結(jié)合在一起形成了一個(gè)勝于部分結(jié)合的整體。其中一個(gè)非常重要的,當(dāng)前也受到格外重視的實(shí)踐就是TDD(測試驅(qū)動的開發(fā)方法)。
在測試驅(qū)動的開發(fā)方法中,編寫所有的代碼的目的都是為了使失敗的單元測試能夠通過。首先編寫一個(gè)單元測試,由于它要測試的功能還不在,所以它會運(yùn)行失敗。然后編寫代碼使測試通過。
編寫測試用例和代碼之間的更迭速度是很快的,基本上幾分鐘左右。測試用例和代碼共同演化,其中測試用例循序漸進(jìn)地對代碼的編寫進(jìn)行指導(dǎo)。作為結(jié)果,一個(gè)非常完整的測試用例集和代碼一起發(fā)展起來。
測試粗略的可以分為單元測試和驗(yàn)收測試。單元測試是用來驗(yàn)證系統(tǒng)中個(gè)別機(jī)制的白盒測試。
單元測試用來驗(yàn)證系統(tǒng)的小的組成單元應(yīng)該按照所期望的方式工作,但是它們沒有驗(yàn)證系統(tǒng)作為一個(gè)整體時(shí)工作的正確性。所以,單元測試是必要的,但是不夠充分。
驗(yàn)收測試是用來驗(yàn)證系統(tǒng)滿足客戶需求的黑盒測試。驗(yàn)收測試由不了解系統(tǒng)內(nèi)部機(jī)制的人編寫。驗(yàn)收測試是程序,因此是可運(yùn)行的。通常通過使用專門為應(yīng)用程序的客戶創(chuàng)建的腳本語言來編寫驗(yàn)收測試。正如單元測試作為可編譯、運(yùn)行的有關(guān)系統(tǒng)內(nèi)部結(jié)構(gòu)的文檔那樣,驗(yàn)收測試是有關(guān)系統(tǒng)特性的可編譯、執(zhí)行的文檔。
編寫代碼前就編寫單元測試會帶來四個(gè)很明顯的好處:
1、首先編寫測試使得程序中的每一項(xiàng)功能都有測試來驗(yàn)證它的操作的正確性。這就可以給以后的開發(fā)提供支援,使我們可以更自由地對程序進(jìn)行更改,因?yàn)闇y試可以告訴我們程序仍然具有正確的行為。
2、首先編寫測試迫使我們必須從程序調(diào)用者的有利視角去觀察我們將要編寫的程序。這樣,我們就會在關(guān)注程序的功能的同時(shí),直接關(guān)注它的接口,我們也就可以設(shè)計(jì)出便于調(diào)用的軟件。
3、首先編寫測試迫使我們把程序設(shè)計(jì)為可測試的。為了把程序設(shè)計(jì)為易于調(diào)用和可測試的,程序必須和它周邊環(huán)境解耦。這樣首先編寫測試迫使我們解除軟件中的耦合。面向?qū)ο笤O(shè)計(jì)的原則在進(jìn)行這種解除耦合方面具有巨大的幫助作用。
4、首先編寫測試的另一個(gè)重要效果是,測試可以作為一種無價(jià)的文檔形式。測試就像一套范例,它幫助其他程序員了解如何使用代碼。這份文檔是可編譯、可運(yùn)行的。它保持最新。它不會撒謊。
首先編寫驗(yàn)收測試的行為對于系統(tǒng)的架構(gòu)方面具有深遠(yuǎn)的影響。為了使系統(tǒng)具有可測試性,就必須要在很高的系統(tǒng)架構(gòu)層面對系統(tǒng)進(jìn)行解耦合。正如單元測試可以促使你在小的方面可以做出優(yōu)良的設(shè)計(jì)決策一樣,驗(yàn)收測試可以促使你在大的方面做出優(yōu)良的系統(tǒng)架構(gòu)決策。
軟件大師、C++之父Bjarne Stroustrup曾經(jīng)說過:設(shè)計(jì)和編程都是人的活動。忘記了這一點(diǎn),將會失去一切。敏捷軟件開發(fā)方法正是認(rèn)識到軟件開發(fā)的這一本質(zhì)特征而提出的革新性開發(fā)方法。使用敏捷開發(fā)方法會給我們帶來巨大的好處。當(dāng)然要完全做到也是很困難的。這不僅需要對敏捷的深刻理解,更需要敏捷團(tuán)隊(duì)成員的共同努力。
本文參考:http://blog.csdn.net/open2job/article/details/6335000
posted @
2013-04-17 17:13 王海光 閱讀(505) |
評論 (0) |
編輯 收藏
摘要: 最近準(zhǔn)備做 Nokia 的 Symbian,Maemo 下觸摸屏開發(fā)。考慮到程序的跨平臺可移植性,最終選擇使用 Qt 開發(fā)。相對來說,國內(nèi)關(guān)于 Qt 相關(guān)文檔并不算很多。作者將 Linux 下編譯并安裝配置 Qt 全過程總結(jié)了一下,只希望可以方便更多的朋友! 1、獲得源代碼  ...
閱讀全文
posted @
2013-04-08 17:07 王海光 閱讀(8638) |
評論 (0) |
編輯 收藏
摘要: 1、$表示普通用戶,#表示超級用戶(root user)。超級用戶是Linux系統(tǒng)中權(quán)限最高的用戶。 2、shell通常以#!起始,例如:#!/bin/bash 3、運(yùn)行腳本的兩種方式:1、將腳本作為sh的命令行參數(shù)。2、將腳本作為具有執(zhí)行權(quán)限的可執(zhí)行文件。 例如:$ sh scri...
閱讀全文
posted @
2013-04-03 16:58 王海光 閱讀(465) |
評論 (0) |
編輯 收藏
雖然網(wǎng)絡(luò)上很多人使用 Redhat
或者fedora
作為上位機(jī)操作系統(tǒng),但是我覺得使用Ubuntu
最為方便,因?yàn)樾枰能浖蟛糠侄伎梢酝ㄟ^ apt-get
方式來安裝,而不必從源代碼開始自己編譯。要知道,自己編譯源代碼可不是一件輕松的事,因?yàn)樵诰幾g過程中經(jīng)常會出現(xiàn)令人意外而且莫名其妙的錯誤。 我們使用的操作系統(tǒng)是 Ubuntu 8.04
,使用目前最新版本的QT 4.4.0
,安裝起來即為簡單,只要運(yùn)行以下命令就行:sudo apt-get install qt4-dev-tools qt4-doc qt4-qtconfig qt4-demos qt4-designer
sudo apt-get install qtcreator
注意在這個(gè)版本的軟件包中,qt4-dev-tools
包含了Qt Assistant
及Qt Linguist
等工具,因此不需要單獨(dú)安裝這兩個(gè)工具。其它的,qt4-doc
是幫助文檔,包含了Qt
中各個(gè)類庫的詳細(xì)說明以及豐富的例子程序,可以使用Qt Assistant
工具來打開閱讀。qt4-qtconfig
是配置Qt
環(huán)境的一個(gè)對話框,一般默認(rèn)就行了,很少有必要去更改。qt4-demos
包含很多可以運(yùn)行起來的可執(zhí)行文件以及源代碼。qt4-designer
是用來設(shè)計(jì)GUI
界面的設(shè)計(jì)器。為了連接MySQL
數(shù)據(jù)庫,需要安裝連接MySQL
的驅(qū)動程序:sudo apt-get install libqt4-sql-mysql
比起在Windows
下安裝和配置Qt
的MySQL
驅(qū)動來說,簡直太方便了。如果還需要其它的沒有默認(rèn)安裝的Qt
庫,可以在命令行輸入 sudo apt-get install libqt4-
然后按tab
鍵自動補(bǔ)全,就會列出所有以libqt4-
開頭的軟件包,如下圖所示:
這些都可以使用一個(gè)命令搞定,而不需要自己從源碼開始編譯。在記不準(zhǔn)或不知道名字的情況下,使用tab
鍵列出所有可選的軟件包是一個(gè)很實(shí)用的小技巧。在我的項(xiàng)目中,還需要畫一些數(shù)據(jù)曲線和統(tǒng)計(jì)圖表等,而第三方的QWT
庫提供了這些功能。同樣,只需要一個(gè)命令即可完成安裝:sudo apt-get install libqwt5-qt4 libqwt5-qt4-dev
這時(shí),打開Qt Designer
,就會發(fā)現(xiàn)左邊的Widget
列表里面多了“Qwt Widget”
這一組。最后,關(guān)于集成開發(fā)環(huán)境我覺得QDevelop
很不錯,它跟Qt Designer
結(jié)合的很好,而且有提示類成員函數(shù)的功能。運(yùn)行以下命令安裝: sudo apt-get install qdevelop
這樣,使用Qdevelop
編寫代碼和編譯、調(diào)試,使用Qt Designer
設(shè)計(jì)界面,開發(fā)效率較高
本文轉(zhuǎn)自:http://blog.csdn.net/zhoufanking/article/details/3278790
posted @
2013-04-02 14:06 王海光 閱讀(611) |
評論 (0) |
編輯 收藏
今天在安裝軟件的時(shí)候出現(xiàn)了Package has no installation candidate的問題,如:
# apt-get install <packagename>
Reading package lists... Done
Building dependency tree... Done
Package aptitude is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
E: Package <packagename> has no installation candidate
解決方法如下:
# apt-get update
【更新同步安裝列表。在這一步之前可能還需要添加新的源,比如
vim /etc/apt/sources.list 我添加了2個(gè)
deb http://mirrors.163.com/ubuntu/ hardy main universe
deb http://tw.archive.ubuntu.com/ubuntu/ hardy main universe
】
# apt-get upgrade【升級所有可升級的已安裝包?我沒做這一步,也可以】
# apt-get install <packagename>
這樣就可以正常使用apt-get了~
本文轉(zhuǎn)自:http://hi.baidu.com/lozard/item/03b854f5a8630015e2e3bd0a
posted @
2013-04-02 13:41 王海光 閱讀(10117) |
評論 (0) |
編輯 收藏
安裝SCIM輸入法的步驟
打開終端,在終端輸入執(zhí)行以下命令:
1).終端輸入:sudo apt-get remove scim
刪除安裝系統(tǒng)的時(shí)候裝的那個(gè)scim(個(gè)人覺得沒什么用,因?yàn)橐话阈掳惭b是操作系統(tǒng)沒有裝這個(gè))
2)sudo apt-get install scim
然后下載安裝scim輸入法。下載完了系統(tǒng)會自己裝上,中間會有提示,直接y就OK了
3)sudo apt-get install scim-chinese
這一步是最重要的,這是下載安裝中文輸入法,前面那一步只是下載一個(gè)可以運(yùn)行輸入法的平臺 ,現(xiàn)在是在這個(gè)具體平臺上安裝
中文輸入法
4) sudo nano /etc/X11/Xsession.d /95xinput
這個(gè)步驟就利用nano新建一個(gè)95xinput的文件 ,該文件的具體內(nèi)容如下:
/usr/bin/scim -d
XMODIFIERS="@im=SCIM"
export XMODIFIERS
export GTK_IM_MODULE=scim
然后,確定無誤后,保存,按ctrl + o,輸入文件名保存 ,ctrl+x 退出
5)exit 退出終端
6)在系統(tǒng)管理中:語言支持的鍵盤輸入方式調(diào)整為scim
7)重新啟動電腦,進(jìn)入系統(tǒng)通過Crtl+Space切換輸入法
本文轉(zhuǎn)自:http://blog.csdn.net/caodesheng110/article/details/7896481
posted @
2013-04-02 13:40 王海光 閱讀(466) |
評論 (0) |
編輯 收藏
.h文件:
#include <string>
using namespace std;
class ZBase64
{
public:
/*編碼
DataByte
[in]輸入的數(shù)據(jù)長度,以字節(jié)為單位
*/
string Encode(const unsigned char* Data,int DataByte);
/*解碼
DataByte
[in]輸入的數(shù)據(jù)長度,以字節(jié)為單位
OutByte
[out]輸出的數(shù)據(jù)長度,以字節(jié)為單位,請不要通過返回值計(jì)算
輸出數(shù)據(jù)的長度
*/
string Decode(const char* Data,int DataByte,int& OutByte);
};
.cpp文件:
#include "stdAfx.h"
#include "ZBase64.h"
string ZBase64::Encode(const unsigned char* Data,int DataByte)
{
//編碼表
const char EncodeTable[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//返回值
string strEncode;
unsigned char Tmp[4]={0};
int LineLength=0;
for(int i=0;i<(int)(DataByte / 3);i++)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
Tmp[3] = *Data++;
strEncode+= EncodeTable[Tmp[1] >> 2];
strEncode+= EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
strEncode+= EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
strEncode+= EncodeTable[Tmp[3] & 0x3F];
if(LineLength+=4,LineLength==76) {strEncode+="\r\n";LineLength=0;}
}
//對剩余數(shù)據(jù)進(jìn)行編碼
int Mod=DataByte % 3;
if(Mod==1)
{
Tmp[1] = *Data++;
strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4)];
strEncode+= "==";
}
else if(Mod==2)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
strEncode+= EncodeTable[((Tmp[2] & 0x0F) << 2)];
strEncode+= "=";
}
return strEncode;
}
string ZBase64::Decode(const char* Data,int DataByte,int& OutByte)
{
//解碼表
const char DecodeTable[] =
{
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
, // '+'
, 0, 0,
, // '/'
, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
, 0, 0, 0, 0, 0, 0,
, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
, 0, 0, 0, 0, 0,
, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
};
//返回值
string strDecode;
int nValue;
int i= 0;
while (i < DataByte)
{
if (*Data != '\r' && *Data!='\n')
{
nValue = DecodeTable[*Data++] << 18;
nValue += DecodeTable[*Data++] << 12;
strDecode+=(nValue & 0x00FF0000) >> 16;
OutByte++;
if (*Data != '=')
{
nValue += DecodeTable[*Data++] << 6;
strDecode+=(nValue & 0x0000FF00) >> 8;
OutByte++;
if (*Data != '=')
{
nValue += DecodeTable[*Data++];
strDecode+=nValue & 0x000000FF;
OutByte++;
}
}
i += 4;
}
else// 回車換行,跳過
{
Data++;
i++;
}
}
return strDecode;
}
使用示例(結(jié)合CxImage庫):CString CScanDlg::EncodeImage()
{//對圖片進(jìn)行Base64編碼
ZBase64 zBase;
//圖片編碼
CxImage image; // 定義一個(gè)CxImage對象
image.Load(this->m_strImgPath, CXIMAGE_FORMAT_JPG); //先裝載jpg文件,需要指定文件類型
long size=0;//得到圖像大小
BYTE* buffer=0;//存儲圖像數(shù)據(jù)的緩沖
image.Encode(buffer,size,CXIMAGE_FORMAT_JPG);//把image對象中的圖像以type類型數(shù)據(jù)copy到buffer
string strTmpResult=zBase.Encode(buffer,size);
CString result;
result = strTmpResult.c_str();
return result;
}
void CScanDlg::DecodeImageData(CString strData)
{//對Base64編碼過的數(shù)據(jù)解碼并顯示原圖片
ZBase64 zBase;
int OutByte=0;
string strTmpResult=zBase.Decode(strData,strData.GetLength(),OutByte);
int i,len = strTmpResult.length();
BYTE *buffer = new BYTE[len];
for (i=0;i<len;++i)
{
buffer[i] = strTmpResult[i];
}
CxImage image(buffer,len,CXIMAGE_FORMAT_JPG);//把內(nèi)存緩沖buffer中的數(shù)據(jù)構(gòu)造成Image對象
delete [] buffer;
CDC* hdc = m_picture.GetDC();
m_bitmap = image.MakeBitmap(hdc->m_hDC);
HBITMAP h0ldBmp = m_picture.SetBitmap(m_bitmap);
if(h0ldBmp) DeleteObject(h0ldBmp);
if(hdc->m_hDC) m_picture.ReleaseDC(hdc);
if(m_bitmap) DeleteObject(m_bitmap);
}
本文轉(zhuǎn)自:http://www.cnblogs.com/phinecos/archive/2008/10/10/1308272.html
posted @
2013-03-20 14:00 王海光 閱讀(11352) |
評論 (0) |
編輯 收藏