青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-5  評論-33  文章-0  trackbacks-0
為了更好的討論這個問題,假設(shè)我們要設(shè)計一個關(guān)于people的class,并進行如下約定:
1. age 小于 0 的baby不在我們的考慮范圍之內(nèi).
2. age 大于 200 的people我們認(rèn)為是不存在的.
即,關(guān)于people的age,我們考慮的范圍是[0 , 200]
基于以上假設(shè),我們設(shè)計如下一個class,這里為了簡化代碼,突出問題的主體,將部分成員函數(shù)及成員變量去掉.

class People
{
    
public:
        
explicit People( int iAge )
        : m_iAge( iAge )
        
{
        
        }

}
;

看起來很簡單,下一步我們可以使用這個class了.

People janice( 28 );
janice.Sing();

如果類似于上面這樣以合法的參數(shù)構(gòu)造一個People,那么后續(xù)的操作也在我們的可控范圍之內(nèi),也是理想的情況.
但是如果我們以非法的參數(shù)構(gòu)造People并進行相關(guān)的函數(shù)調(diào)用,如下:

People hebe( -1 );
hebe.Sing();

People ella( 
201 );
ella.Dance();


那么這樣實在是太糟糕了,因為在調(diào)用Sing()和Dancing()時我們甚至不知道調(diào)用的對象的內(nèi)部狀態(tài)是非法的,這也就成為bug的一個源泉.

總結(jié)一下上面的例子中遇到的問題:在以特定的參數(shù)構(gòu)造一個對象時,如果參數(shù)非法或構(gòu)造失敗,我們應(yīng)當(dāng)向調(diào)用者反饋這一信息.

對于一般的成員函數(shù),如果能夠有返回值,那么我們可以通過返回值來標(biāo)識傳遞給函數(shù)的參數(shù)非法或內(nèi)部運行失敗這種情況.
但是對于構(gòu)造函數(shù),因為它不能有返回值,所以,我們必須使用其它的方法來向調(diào)用者反饋"傳遞給構(gòu)造函數(shù)的參數(shù)非法或構(gòu)造失敗".

針對于這一問題有三種解決方案:

第一種方案:在構(gòu)造函數(shù)的參數(shù)中傳遞一個額外的參數(shù)用于標(biāo)識構(gòu)造是否成功.
在這種方案的指導(dǎo)下,代碼如下:

class People
{
    
public:
        People( 
int iAge , bool &bInitStatus )
        : m_iAge( iAge )
        
{
            
if( ( iAge < 0 ) || ( iAge > 200 ) )
            
{
                bInitStatus 
= false;
            }

            
else
            
{
                bInitStatus 
= true;
            }

        }

}
;


然后我們可以這樣使用:

bool bInitStatus = false;
People hebe( 
-1 , bInitStatus );
iffalse == bInitStatus )
{
    
// handle error
}

else
{
    hebe.Sing();
}


這種方法是可行的,但是代碼看起來過于丑陋且不夠直觀,這里只是作為一種方案提出并不推薦使用.

第二種方案:使用兩段構(gòu)造的形式.
所謂的兩段構(gòu)造是指一個對象的內(nèi)部狀態(tài)的初始化分兩步完成,將構(gòu)造函數(shù)中的部分初始化操作轉(zhuǎn)移到一個輔助初始化的成員函數(shù)中:
第一步是通過構(gòu)造函數(shù)來完成部分內(nèi)部狀態(tài)的初始化.
第二步是通過類似于 Initialize 之類的成員函數(shù)來完成對象內(nèi)部狀態(tài)的最終初始化.

兩段構(gòu)造的形式在 MFC 中廣泛使用.在MFC中我們經(jīng)常看到類似于 Initialize , Create 之類的函數(shù).
基于兩段構(gòu)造的形式,代碼如下:

class People
{
    
public:
        People( 
void )
        : m_iAge( INVALID_AGE )
        
{
        
        }

        
        
bool Initialize( int iAge )
        
{
            
if( ( iAge < 0 ) || ( iAge > 200 ) )
            
{
                
return false;
            }

            
else
            
{
                m_iAge 
= iAge;
                
return true;
            }
            
        }

}
;


在這種情況下,我們應(yīng)當(dāng)這樣來使用People:

People hebe;
const bool bStatus = hebe.Initialize( 20 );
iffalse == bInitStatus )
{
    
// handle error
}

else
{
    hebe.Sing();
}


這種方案似乎比第一種方案更優(yōu),但是仍有一個潛在的問題:對象是以兩步構(gòu)造完成的.
第一步構(gòu)造是由構(gòu)造函數(shù)來完的,OK,這一點我們不用擔(dān)心,編譯器幫我們保證.
但是第二步是由類似于 Initialize 之類的成員函數(shù)來完成的,如果我們在構(gòu)造一個People對象之后忘記了調(diào)用 Initialize ,
那么這個對象的內(nèi)部狀態(tài)仍然是非法的,后續(xù)的操作也將由此引發(fā)bug.這也是"兩段構(gòu)造"這種形式受到詬病的原因之一.
另一方面,"兩段構(gòu)造"的形式與C++的"RAII",Resource Acquisition Is Initialization(資源獲取即初始化),這一原則相違背.
因為以"兩段構(gòu)造"這種形式設(shè)計的class People 在構(gòu)造一個對象時,它的內(nèi)部狀態(tài)實際上并沒有完全初始化,我們需要調(diào)用 Initialize 來輔助完成最終的初始化.
所以,盡管"兩段構(gòu)造"這種方案可以解決我們所遇到的"對構(gòu)造函數(shù)參數(shù)非法進行反饋"這個問題,但是這種方案并不夠優(yōu)雅.

但是為什么MFC會先擇"兩段構(gòu)造"這種形式呢,因為在C++發(fā)展的初期,當(dāng)異常機制還不是足夠成熟,沒有得到廣泛的認(rèn)可和使用時,
MFC中選擇兩段構(gòu)造或許也是情理之中的,也許還有其它的原因,類似的類庫還有ACE...

當(dāng)然,在某些情況下,使用兩段構(gòu)造也有其獨到的好處.
下面所設(shè)計的場景可能有一些牽強,但只是為了力求簡單并能夠說明問題.(代碼進行了大量簡化)

class Server
{
    
public:
        Server(
void)
        
{
            
// allocate a huge chunk of memory to store data
        }

        
        
~Server( void )
        
{
            
// free all the used resource
        }

}
;


然后在我們的系統(tǒng)中,我們需要使用一個 server pool , 在系統(tǒng)啟動時,我們需要 server pool 中有 100 個 Server 可用.

Server serverPool[ 100 ];


在系統(tǒng)負(fù)載最大的時候,假定100個Server可以勝任,但是在大多數(shù)情況下,我們只需要少量的Server即可以完成任務(wù).
在這種情況下: Server serverPool[ 100 ]; 將會消耗大量的資源(而且大部分資源我們并不會使用),這是我們不愿意接受的.
之所以出現(xiàn)這種情況,因為我們在構(gòu)造函數(shù)中分配了大量資源,這種分配是隨構(gòu)造函數(shù)的調(diào)用而自動完成的.


這時,如果我們使用"兩段構(gòu)造"的方法就能在一定的程度上解決這個問題.

class Server
{
    
public:
        Server(
void)
        
{
            
// do nothing here.
        }

        
        
bool Initialize( void )
        
{
            
// allocate a huge chunk of memory to store data
        }

        
        
~Server( void )
        
{
            
// free all the used resource
        }

}
;


在這種情況下: Server serverPool[ 100 ]; 的開銷就很小了,我們可以很好地控制對系統(tǒng)資源的使用,而不會浪費.
當(dāng)然,當(dāng)我們從 serverPool 中獲取一個 Server 對象時,我們要調(diào)用 Initialize 進行最終的初始化操作.


第三種方案:使用異常
即是當(dāng)用于構(gòu)造 People 對象的參數(shù)非法時,我們選擇在構(gòu)造函數(shù)中拋出一個異常來反饋給調(diào)用者"參數(shù)非法,構(gòu)造失敗"的相關(guān)信息.

class People
{
    
public:
        
explicit People( int iAge ) throw( std::invalid_arguement )
        : m_iAge( iAge )
        
{
            
if( ( iAge < 0 ) || ( iAge > 200 ) )
            
{
                
throw std::invalid_arguement( "invalid argument" );
            }

        }

}
;


那么我們可以這樣使用:

try
{
    People hebe( 
20 );
    hebe.Sing();
}

catchconst std::invalid_arguement &refExcept )
{
    
// handle exception here.
}


這種方案似乎是最優(yōu)的:符合RAII原則,也符合B.S等一批老大推行的"現(xiàn)代C++程序設(shè)計風(fēng)格".

但是很多在開發(fā)一線上的同學(xué)們都反對在代碼中使用異常,實際上我也不愿意在代碼中使用異常,
至少不愿意看到類似于java代碼中那樣鋪天蓋地的"throw try catch".
我對異常的使用也僅僅是局限在類似于那些適合"用異常來代替兩段構(gòu)造"的場景中,對于其它的情況,
我更愿意用返回錯誤碼來標(biāo)識函數(shù)內(nèi)部的運行狀態(tài),而不是通過拋出異常的形式來告知調(diào)用者.


C++規(guī)定:如果執(zhí)行構(gòu)造函數(shù)的過程中產(chǎn)生異常,那么這個未被成功構(gòu)造的對象的析構(gòu)函數(shù)將不會被調(diào)用.
這一點在很大程度上為我們在構(gòu)造函數(shù)中拋出異常的安全性提供了C++語言級的保證,當(dāng)然,其它的安全性需要我們自己保證.

對于"向調(diào)用者反饋構(gòu)造函數(shù)參數(shù)非法或構(gòu)造失敗的相關(guān)信息"這個問題,"基于異常"和"基于兩段構(gòu)造"這兩種方案我都使用過一段時間,
目的是確定對于自己而言到底哪一種方案用起來更舒服更適合自己.最終的結(jié)果是我選擇了"基于異常"這種形式.

對于"基于異常"和"基于兩段構(gòu)造",沒有哪一種能在所有的情況下都是最優(yōu)的解決方案,印證了那句名言"there is no silver bullet".
如何在不同的場景中選擇其一作為最優(yōu)的解決方案是我們在設(shè)計時需要權(quán)衡的問題.


個人愚見,錯漏之處還請指正,歡迎大家踴躍發(fā)言:)

posted on 2010-03-04 21:23 luckycat 閱讀(2826) 評論(18)  編輯 收藏 引用 所屬分類: C++

評論:
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-04 22:03 | Corner Zhang
呵呵!如果你要傳統(tǒng)的編程風(fēng)格,就用Initialize() Shutdown()的“兩段構(gòu)造”;若要趨向于捕捉異常的try catch的風(fēng)格,就用構(gòu)造器上的異常處理唄!

具體取舍,看項目人員本身素質(zhì),按我的經(jīng)驗,傳統(tǒng)風(fēng)格的代碼易于調(diào)試跟蹤,錯誤就在附近。  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-04 22:22 | qiaojie
兩種方法都是錯誤的用法,這地方應(yīng)該使用斷言而不是異常或者錯誤返回值。傳入錯誤的參數(shù)值是程序的BUG而不是真正的異常,異常是由于外部運行環(huán)境的錯誤引起而非程序本身的BUG(例如,內(nèi)存耗盡,網(wǎng)絡(luò)錯誤,文件錯誤等等),異常處理只應(yīng)該用在真正屬于異常的情況。當(dāng)然,有的時候People的age參數(shù)來自于用戶的輸入,這個時候也不應(yīng)該使用異常,而是在用戶輸入完成,對話框結(jié)束,構(gòu)造People之前,加入一個ValidateUserInput()函數(shù)來校驗用戶輸入,如果age屬于非法值,彈出一個錯誤對話框向用戶說明錯誤的原因。  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-04 22:32 | luckycat
@Corner Zhang
關(guān)于這一點,我們的觀點還是很相近的,不同的編程風(fēng)格決定了不同的設(shè)計取舍以及代碼風(fēng)格,
沒有哪一種一直都是最優(yōu)的,選擇一種適合自己的并一直堅持下去(當(dāng)然,適當(dāng)?shù)臅r候還是要變通一下).
實際上我并不愿意在代碼中大量使用"throw try catch",所以,基本上我不愿意去看java代碼,
就如你所說的,傳統(tǒng)的代碼風(fēng)格易于跟蹤調(diào)試;在我看來,傳統(tǒng)的"基于錯誤碼"的代碼比基于"使用異常"的代碼
要緊湊得多,因為我們可以在錯誤發(fā)生的地方立即處理錯誤,而不像"基于異常"的代碼中我們要向下跨越N行代碼
來進行錯誤處理(這一點使得代碼的可讀性很差).
而且,如果try代碼塊中太大,那么在對應(yīng)的catch塊中盡管我們可以進行相應(yīng)的異常處理,但是此時我們卻失去了
對發(fā)生錯誤的代碼上下文的必要了解.這一點使得我們降低了對代碼整體運行流程的可預(yù)知性,
更重要的是也降低了錯誤處理的針對性,因為同一種類型的異常可能由try代碼塊中的多個地方throw.具體是哪一個throw的無從了解.
so,我的觀點是:讓構(gòu)造函數(shù)盡量簡單,減少誤用的可能性,并增加構(gòu)造函數(shù)的安全性(盡量減少構(gòu)造函數(shù)構(gòu)造失敗的可能性).
這樣我們也就能在一定程度上減少對異常機制的依賴.至于其它的可帶有返回值的成員函數(shù)都使用"返回錯誤碼"來取代"拋出異常".
  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-04 23:25 | luckycat
@qiaojie
呵呵,可不能一棒子打死啊.
至于說,類似于"用戶輸入非法數(shù)據(jù)"之類的問題到底是算作錯誤還是異常情況,這一點依賴每個人對同一個事物的認(rèn)知,
有的人認(rèn)為這是異常情況,有的人認(rèn)為這是錯誤,這個認(rèn)知層面上的問題我們先不討論,尊重每個人的看法.
實際上,即使存在上面的認(rèn)知差異也沒有關(guān)系,因為問題的本質(zhì)是對"用戶輸入非法數(shù)據(jù)"這種異常也好,錯誤也好,
我們在代碼邏輯中應(yīng)該如何處理,你覺得應(yīng)該用類似于"對話框+ValidateUserInput"之類的方法來處理,
我覺得可以通過返回錯誤碼或拋出異常的形式來做處理. 本質(zhì)上都是在處理一種"非正常情況",只是我們的處理方式不同,
你說應(yīng)該用你的方法比較好,我覺得用我的方法處理也是可行的,到底用哪一種呢,
即使在這種情況下,我們還是很難選擇一個所有人都接受的處理方式. 這里就涉及到設(shè)計的權(quán)衡和取舍了,有很多種方法都可行,我們尊重每個人在特定的環(huán)境中所做出的選擇.

/*
"而是在用戶輸入完成,對話框結(jié)束,構(gòu)造People之前,加入一個ValidateUserInput()函數(shù)來校驗用戶輸入,
如果age屬于非法值,彈出一個錯誤對話框向用戶說明錯誤的原因"
*/
你這里只是對一種特例的處理,實際上我們很難在所有的情況都保證傳入構(gòu)造函數(shù)的參數(shù)是合法的,要是我們真的找到了這樣一種方法,
那么"there is a silver bullet !" , 接下來,對于所有奮斗在開發(fā)一線的同學(xué)們而言,生活就要美好很多,應(yīng)該再也不會發(fā)生類似于"小貝"的悲劇了:)

在你處理的特例中,既然我們能夠保證傳入構(gòu)造函數(shù)的參數(shù)一定是合法的,那確實太好了,"使用異常"和"兩段構(gòu)造"都是多余的.
對于那種我們不能確保傳入構(gòu)造函數(shù)的參數(shù)是一定是合法的情況,我們該選擇哪種處理方式呢,這是這篇文章討論的根本問題.
如果因為構(gòu)造函數(shù)的參數(shù)不合法,或者因為其它的原因構(gòu)造失敗,最基本的一點,我們應(yīng)當(dāng)讓調(diào)用者知道這一情況,
至于調(diào)用者如何處理就不在我們關(guān)心的范圍之內(nèi)了,是"彈出對話框告知用戶重試","忽略這個錯誤",還是直接"abort",不同的場景下也有不用的選擇.
我們要做的就是在"構(gòu)造一個對象發(fā)生異常時"告知調(diào)用者"發(fā)生了非正常情況".

這篇文章的主題也就是討論"在構(gòu)造發(fā)生非正常情況時采取何種方式來告知調(diào)用者這一情況".
對于這個問題很難有一個"放之四海而皆準(zhǔn)"的處理方案,因為這涉及到不同的編程風(fēng)格,應(yīng)用場景和設(shè)計時的取舍.

不過我們還是可以踴躍地發(fā)表自己的看法,在討論和交流的過程中我們總能發(fā)現(xiàn)思維的閃光點.互相學(xué)習(xí):)  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 08:34 | 飯中淹
我一般會用一種類似工廠的方法。

class CPeople
{

int m_iAge;

CPeople() : m_iAge(-1) {}
~CPeople() {}
bool _Init( int iAge )
{
if( iAge <= 0 || iAge > 200 )return false;
m_iAge = iAge;
return true;
}
public:
static CPeople * Create( int iAge )
{
CPeople * people = new CPeople();
if( people != NULL &&
!people->_Init(iAge) )
{
people->Release();
people = NULL;
}
return people;
}
void Release() { delete this;}
};


私有的構(gòu)造函數(shù)和西狗函數(shù)確保他們不能被單獨分配和刪除
所有的途徑只有create和release。

  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 09:04 | LOGOS
@飯中淹
agree
這正是我想說的。另外,在一些情形下構(gòu)造函數(shù)不易調(diào)試,而兩段構(gòu)造則能避開這一調(diào)試,選擇更好的調(diào)試的init  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 10:31 | 肥仔
@飯中淹
每個對象都要顯示的create和release。那基本上等價于放棄了C++構(gòu)造函數(shù)和析構(gòu)函數(shù)這兩個特性。
對于棧對象,這樣會很累,也很容易泄漏。  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 12:02 | luckycat
@飯中淹
將"兩段構(gòu)造"與"Fatcory Pattern"結(jié)合起來確實是一種巧妙的設(shè)計!
內(nèi)部實現(xiàn)上還是"兩段構(gòu)造",但是對于 class 的用戶而言,class CPeople 展現(xiàn)的卻是一個單一的"構(gòu)造接口",
用戶一旦調(diào)用這個接口"構(gòu)造對象",那么"兩段構(gòu)造"自動完成,極大地減少了"兩段構(gòu)造"中因為忘記調(diào)用"Initialize"所帶來的問題.
class CPeople 中的 Create 和 Release 所扮演的角色類似于"構(gòu)造函數(shù)和析構(gòu)函數(shù)",都是進行資源的分配與回收操作.
單純從"資源管理"的角度來說,肯定是"構(gòu)造函數(shù)和析構(gòu)函數(shù)"相比如"Create 和 Release"更優(yōu)一些,
因為"構(gòu)造函數(shù)和析構(gòu)函數(shù)"對于"非動態(tài)分配的對象以及非placement new方式生成的對象",
構(gòu)造和析構(gòu)都會由編譯器保證正確自動地調(diào)用,大大簡化了對資源的管理,或許這也是C++設(shè)計構(gòu)造和析構(gòu)的出發(fā)點之一.

在"兩段構(gòu)造" & "Fatcory Pattern"這種模式下,所有的CPeople對象將都由 Create 接口創(chuàng)建,這勢必需要我們管理大量的動態(tài)分配的對象,
在這種情況下,如果稍有不慎,我們將面臨"resource leak"的問題.這個時候如果我們能將動態(tài)分配的CPeople對象用一種更方便安全的方式來管理就更好了,
于是我想到了boost::shared_ptr,不知道大家想到了什么?
類似于下面這樣:

void FreeResource( CPeople *pPeople )
{
if( NULL != pPeople )
{
pPeople -> Release();
}
}

CPeople *pHebe = CPeople::Create( 2 );
if( NULL == pHebe )
{
// handle error
}

boost::shared_ptr< CPeople > pPeople( pHebe , FreeResource );

下面我們就可以使用 pPeople 這個智能指針"do whatever you want" :) ,而且使用起來直觀方便:
pPeople -> Sing();
也減少了對動態(tài)分配資源進行管理的復(fù)雜度.



  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 17:36 | 望見
為了程序的健壯性,多余的操作也是必須的。  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 18:00 | qiaojie
@luckycat
你的根本問題在于你沒理解異常的使用哲學(xué),在錯誤的前提下去談什么設(shè)計的選擇根本就是一件毫無意義的事情。你如果要堅持自己的錯誤認(rèn)識,去談什么尊重每個人的選擇,那我只能自認(rèn)是對牛彈琴了。

再來看為什么我說要用ValidateUserInput而不是其他什么方法,因為驗證用戶的輸入通常來說都是一個復(fù)雜的且經(jīng)常可變的需求,我們假設(shè)Person的構(gòu)造參數(shù)里有一個UserName,通常UserName的驗證會比較復(fù)雜,有長度限制,非法字符限制,重名限制等等,對于重名限制往往還要去用戶管理器或者后臺數(shù)據(jù)庫查詢一下,現(xiàn)在來看看把這些驗證邏輯都寫到Person的構(gòu)造函數(shù)里是多么傻X的做法啊,首先,Person的構(gòu)造函數(shù)需要依賴用戶管理器或者后臺數(shù)據(jù)庫才能驗證UserName的合法性。其次,當(dāng)你的構(gòu)造函數(shù)返回了錯誤值或者異常的時候,外部的處理代碼卻根本不知道為什么這個用戶名是非法的,所以要么還得再寫一遍驗證邏輯來檢查哪里非法,要么直接告訴用戶輸入非法,讓用戶摸不著頭腦。
  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 19:33 | luckycat
@qiaojie
也許你的異常哲學(xué)是正確的并值得大家學(xué)習(xí),還請你發(fā)文一篇讓大家有一個學(xué)習(xí)的機會,
如果從你的文章中我確實發(fā)現(xiàn)了自己的錯誤,也會從中有所改正,當(dāng)然,你也不需要"對牛彈琴"這個詞語.
我這篇文章中的"People"只是一個用于作為討論基礎(chǔ)的例子,根本的問題是對于"構(gòu)造函數(shù)的參數(shù)非法或是構(gòu)造失敗"時,我們應(yīng)當(dāng)如果告知調(diào)用者.
我并沒有說一定要把參數(shù)的合法性全部放在構(gòu)造函數(shù)中完成,但是在構(gòu)造函數(shù)中檢查參數(shù)的合法性是應(yīng)該的,
就像上面的同學(xué)說的"為了程序的健壯性,多余的操作也是必須的"。
在這個例子中,你可以往自己熟悉的GUI方向進行特化,所以你可以使用"對話框"之類的工具來進行傳入構(gòu)造函數(shù)之前的參數(shù)合法性檢驗以及進行相關(guān)的錯誤處理,
但是在那些"非GUI"的領(lǐng)域,在那些"我們不能確保傳入構(gòu)造函數(shù)的參數(shù)一定是合法的,不能保證構(gòu)造函數(shù)一定會構(gòu)造成功"的情況下,我們到底該如何處理,
我考慮到可以使用"基于異常"或"基于兩段構(gòu)造的形式".
C++提供的異常機制是一種工具,可以作為"函數(shù)內(nèi)部向函數(shù)的調(diào)用者傳遞函數(shù)內(nèi)部非正常運行狀態(tài)"的一種方法.
就如同你說的"內(nèi)存耗盡,網(wǎng)絡(luò)錯誤,文件錯誤"這種情況下是異常,也許這種情況下我們應(yīng)當(dāng)使用"異常機制"(希望沒有理解錯).
但是如果一個函數(shù)內(nèi)部可能出現(xiàn)"內(nèi)存耗盡"也會出現(xiàn)"參數(shù)非法的問題"(再重申一遍,我們不能永遠都保證傳入每一個函數(shù)的參數(shù)都是合法的).
"內(nèi)存耗盡"這種情況我們使用異常,但是"參數(shù)非法問題"我們使用什么呢,
按照你的看法,"參數(shù)非法"不屬于異常的范圍之內(nèi),我們不應(yīng)該使用"異常的形式",但我們還是要告知用戶"參數(shù)非法"的信息,
假定這里我們"無法使用類似于彈出對話框的形式來告知用戶參數(shù)非法",那么我可以想到的告知調(diào)用者這一信息的方式是"使用錯誤碼",
當(dāng)然,我們還可以選擇"errno"的形式.
這樣一來,我們就面臨一個問題"一個函數(shù)會以異常和錯誤碼兩種方式來告知調(diào)用者相關(guān)的非正常運行信息",
接下來,調(diào)用者就要同時使用"try catch"和檢查函數(shù)的錯誤碼兩種方式來檢查函數(shù)的運行狀態(tài),
我覺得如果真的這樣設(shè)計函數(shù)的話,這就是一種很糟糕的設(shè)計,不知道你怎么認(rèn)為.

在告知調(diào)用者一個函數(shù)內(nèi)部的"非正常狀態(tài)"時,我只會擇優(yōu)使用"錯誤碼"或"異常這兩種形式"之一,不會同時使用.
基于這一點,如果我選擇"以錯誤碼的形式"來反饋給調(diào)用者,那么在函數(shù)內(nèi)部"網(wǎng)絡(luò)錯誤"時我也會使用錯誤碼來告知調(diào)用者(按你的看法,這種情況應(yīng)該使用異常),
如果我選擇"基于異常"的形式,那對"參數(shù)非法"的信息我也會拋出"std::invalid_arguement".這是設(shè)計上的取舍產(chǎn)生的必然選擇.


說到這里,不知道你對于作為std異常類型之一的"std::invalid_arguement"這個詞語有什么感想,
我覺得你應(yīng)該向標(biāo)準(zhǔn)委員會指明"std::invalid_arguement"這個詞語,
"從使用異常的哲學(xué)上的角度上來看這個概念是錯誤的,因為參數(shù)非法根本就不是異常,我們又怎么能因為參數(shù)的非法而throw std::invalid_arguement,
這是在誤導(dǎo)廣大的std用戶,所以必須去掉".  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 21:56 | qiaojie
@luckycat
保證參數(shù)的正確性是調(diào)用者的責(zé)任,而不是被調(diào)用的函數(shù)的責(zé)任,如果你對此有疑問的話可以去閱讀一下契約式編程。當(dāng)然,我并沒有說不需要去檢查參數(shù)的合法性,而是強調(diào)應(yīng)該用assert(0 < age && age < 200);這樣的斷言來檢查參數(shù)合法性,這個為什么強調(diào)不要用異常或者錯誤返回值,是因為盲目的大量使用這類錯誤處理機制會導(dǎo)致整個項目變得混亂,如果任意一個帶參數(shù)的函數(shù)都會因為參數(shù)非法而拋異常,那么我在外面接到異常的時候會非常困惑,到底該如何正確處理那么多可能出現(xiàn)的異常?如果是用錯誤返回值的話就更麻煩,每個函數(shù)調(diào)用都要進行檢查,代碼及其繁瑣。錯誤處理機制是整個軟件設(shè)計的重要環(huán)節(jié),他不像接口設(shè)計那么顯而易見,所以在設(shè)計上更應(yīng)該小心規(guī)劃合理使用,在
哪里會出異常,哪里該接異常應(yīng)該做到心中有數(shù)正確處理,否則就會陷入混亂。
當(dāng)然,凡事無絕對,我說的這種使用方式并不適用于組件級或者系統(tǒng)級程序,系統(tǒng)級程序必須要采用防御性編程策略來檢測參數(shù)的合法性并向調(diào)用者報告錯誤(因為無法預(yù)期調(diào)用者如何調(diào)用函數(shù)),這常需要付出代價,常導(dǎo)致系統(tǒng)級提供的API接口跟應(yīng)用程序之間阻抗適配,需要在應(yīng)用程序內(nèi)進行適當(dāng)?shù)姆庋b。而你的例子顯然不屬于這類程序。

另外你拿std::invalid_arguement出來我覺得毫無意義,C++標(biāo)準(zhǔn)里不成熟不完善甚至很爛的東西多了去,說上三天三夜也說不完。C++的異常機制尤其不完善,備受爭議。像std::invalid_arguement基本沒人會去用。  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-05 22:40 | luckycat
@qiaojie
謝謝賜教!

"像std::invalid_arguement基本沒人會去用"這句話說得有點絕對了,用的人應(yīng)該還是有一些的,可能我們沒有接觸到.
另外,我們總是被"天朝"代表,想不到這次被 qiaojie 代表了:)

你說"保證參數(shù)的正確性是調(diào)用者的責(zé)任,而不是被調(diào)用的函數(shù)的責(zé)任",
這一點我也同意,不過我覺得作為函數(shù)的設(shè)計者,我們不應(yīng)當(dāng)對用戶所傳遞的參數(shù)有太多理想化的假設(shè),所以我們應(yīng)當(dāng)在函數(shù)中進行參數(shù)合法性的檢查,
一方面,可以在函數(shù)的入口處盡早發(fā)現(xiàn)非法參數(shù)的問題,這樣就不至于后續(xù)會使用錯誤的參數(shù)在函數(shù)中進行一些無意義的操作.
另一方面,在函數(shù)入口處檢查參數(shù)的合法性,可以增強函數(shù)的健壯性,進一步增強系統(tǒng)的健壯性.
舉個例子,如果傳遞給函數(shù)的實參不應(yīng)該是NULL指針,用戶卻以NULL作為實參調(diào)用函數(shù),假設(shè)我們沒有進行對應(yīng)參數(shù)合法性檢查,
那么后續(xù)基于這個NULL實參的操作可能會導(dǎo)致系統(tǒng)"coredump".

對于參數(shù)的合法性檢查,在debug版本和release版本下應(yīng)該都需要進行,類似于"assert(0 < age && age < 200);"這種檢測參數(shù)的合法性的代碼只在debug版本下可以起作用,
在release版本下就不起用了,也就不能在release版本下作為參數(shù)合法性檢查的工具.
在debug版本下,如果assert斷言失敗,那么我們可以看到對應(yīng)的abort信息,然后程序異常退出.
實際上這樣做可能有的時候并不合適,因為在一些情況下,僅僅是參數(shù)非法,我們可以進行相應(yīng)的處理而不需要系統(tǒng)因此而退出運行.

"強調(diào)不要用異常或者錯誤返回值,是因為盲目的大量使用這類錯誤處理機制會導(dǎo)致整個項目變得混亂"
這句話如果僅僅是理論上來探討"如何讓系統(tǒng)設(shè)計的更優(yōu)雅",那么這無疑可以作為一個"系統(tǒng)設(shè)計準(zhǔn)則",
但是在實際的開發(fā)過程中,有的時候一個函數(shù)內(nèi)部出現(xiàn)"非正常情況"的可能性實在是太多了,我們必須要進行相應(yīng)的處理.
如果我們既不使用"異常"也不使用"返回錯誤碼"的形式來告知調(diào)用者,
那么在反饋給調(diào)用者"函數(shù)內(nèi)部出現(xiàn)非正常情況"這一點上我們將"無能為力",但我們又必須在這一點有所作為.

在大多數(shù)情況下,"異常"和"錯誤碼"可能是我們僅有的兩個選擇方案,如何選擇其一作為最終的處理方案,
甚至如何在不使用"異常"和"錯誤碼"的前提下也達到相同的效果,這是一件很"糾結(jié)"的事情.


追求系統(tǒng)在架構(gòu)和代碼設(shè)計上的完美是開發(fā)者的一個方向,但是有時我們需要考慮"追求完美的代價",
在時間,人力以及成本的多重影響下,很多時候我們必須放棄對最優(yōu)方案的探索,而選擇一種"不那么完美但是可行,可以很好解決問題"的方案.
也許這個時候作為函數(shù)調(diào)用狀態(tài)反饋的"異常"和"錯誤碼"機制會在我們的思考和運用范圍之內(nèi).


  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-06 08:55 | chentan
qiaojie 討論很精彩
我的習(xí)慣是 模塊邊界會檢查參數(shù)合法性,并報告給調(diào)用者
模塊內(nèi)部的代碼盡量多的用assert  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-06 21:06 | 陳梓瀚(vczh)
對于類庫的設(shè)計者,其實有一個很簡單的判斷標(biāo)準(zhǔn)。

1:如果一個輸入錯誤,調(diào)用者必須知道并處理,那么就采用“不處理就崩潰”方法,迫使調(diào)用者處理。在C++里面的唯一方法就是使用異常了。舉個例子,對一個容器的下標(biāo)訪問越界,這個時候operator[]不可能返回一個可用的返回值,而且也不能用錯誤信息去污染返回值,譬如返回pair<bool, T>,因此直接拋出異常。

2:如果一個構(gòu)造函數(shù)發(fā)生錯誤之后,這個對象是絕對不能被碰的,那么采用異常。因為在try-catch結(jié)構(gòu)里面,構(gòu)造函數(shù)完蛋了,那么那個正在被構(gòu)造的對象你是沒有辦法獲取的。

3:異常可以用來在大量函數(shù)互相遞歸的時候(譬如說語法分析器)迅速跳到最外層,此處作為控制代碼流程而使用。

這里我也接受一個例外,譬如說2,如果構(gòu)造函數(shù)發(fā)生錯誤但是我并不想立刻拋出異常(因為有些狀態(tài)在構(gòu)造函數(shù)里面想知道也比較麻煩),那么除了需要一個類似bool IsAvailable()const函數(shù)來告訴你以外,所有該類成員的非法操作(譬如說在錯誤的構(gòu)造狀態(tài)下調(diào)用一個成員函數(shù))都必須拋出異常,或者【絕對不產(chǎn)生任何副作用】的同時給一個返回值說明調(diào)用失敗。  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-06 21:27 | luckycat
@陳梓瀚(vczh)
你列舉的判斷標(biāo)準(zhǔn)都值得借鑒,不過你后續(xù)補充的對"例外情況"的處理方式不敢茍同:
因為構(gòu)造可能沒有成功,那么我們需要調(diào)用IsAvailable之類的函數(shù),甚至于后續(xù)需要因為判斷之前構(gòu)造函數(shù)的狀態(tài)來
對調(diào)用的每個成員函數(shù)進行"try catch"或者還要從"每個成員函數(shù)的返回值中來判斷之前的構(gòu)造操作是否成功".
這種設(shè)計是可行的,但對類的使用者來說太復(fù)雜.
這種情況下我覺得使用"兩段構(gòu)造"可能更好一些,我們只需要判斷"兩段構(gòu)造"是否成功即可,如果構(gòu)造成功,在后續(xù)的成員函數(shù)調(diào)用過程中,
就再也不用為了確認(rèn)構(gòu)造函數(shù)的狀態(tài)來對每個被調(diào)用的成員函數(shù)進行"try catch"或檢查返回值的操作,這樣的設(shè)計應(yīng)該更簡潔一些.  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2010-03-08 01:28 | 陳梓瀚(vczh)
@luckycat
兩段構(gòu)造的話,你的類的其他成員函數(shù)也應(yīng)該在構(gòu)造不成功的時候拋出異常,并沒有任何區(qū)別,我的例外還減少了別人可能忘記第二段構(gòu)造的概率。

當(dāng)然這是對一個庫的要求,應(yīng)用程序不一定要如此嚴(yán)格。  回復(fù)  更多評論
  
# re: 設(shè)計的兩難:選擇異常還是兩段構(gòu)造 2015-01-27 23:09 | tsgsz
拋異常不析構(gòu)可以令所有的成員變量都是 std::unique_ptr  回復(fù)  更多評論
  

只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲精品一区二区三区av| 久久人人爽爽爽人久久久| 亚洲第一页在线| 国产精品久久久久毛片软件| 久久综合婷婷| 欧美在线一二三四区| 99热在线精品观看| 亚洲电影自拍| 久久久在线视频| 小处雏高清一区二区三区| 亚洲免费高清视频| 欲香欲色天天天综合和网| 国产精品永久免费在线| 欧美三区在线视频| 欧美日本三级| 欧美国产一区视频在线观看 | 久久国产精品电影| 亚洲视屏一区| 一区二区三区黄色| 亚洲免费电影在线| 亚洲国产激情| 亚洲国产日韩欧美| 欧美激情精品久久久久久黑人| 久久综合久久综合久久综合| 欧美一级网站| 欧美一区成人| 久久丁香综合五月国产三级网站| 午夜日韩电影| 欧美一级午夜免费电影| 欧美亚洲视频| 欧美一区高清| 久久av资源网| 久久久午夜电影| 久久天天狠狠| 蜜桃av综合| 欧美黄色aa电影| 亚洲国产三级| 亚洲美女毛片| 一本色道**综合亚洲精品蜜桃冫 | 亚洲欧洲一区二区在线播放| 亚洲第一福利视频| 亚洲国产精品福利| 亚洲日本va在线观看| 亚洲精品久久久久久久久久久久 | 久久精品国产亚洲5555| 久久国产视频网| 久久综合网hezyo| 欧美成人在线免费观看| 亚洲成人自拍视频| 99精品99| 午夜久久福利| 浪潮色综合久久天堂| 欧美国产大片| 欧美视频你懂的| 国产日韩一级二级三级| 136国产福利精品导航| 亚洲精品欧美一区二区三区| 亚洲视频在线一区观看| 性欧美长视频| 欧美sm重口味系列视频在线观看| 欧美激情影音先锋| 亚洲少妇自拍| 久久久久久高潮国产精品视| 欧美激情无毛| 国产人成一区二区三区影院| 亚洲高清av| 亚洲午夜精品一区二区三区他趣| 欧美在线视频一区二区| 六月婷婷久久| 中日韩美女免费视频网址在线观看 | 久久综合99re88久久爱| 欧美日韩高清在线观看| 国产亚洲欧美激情| 日韩一区二区久久| 欧美在线观看你懂的| 欧美激情欧美激情在线五月| 亚洲网站在线播放| 噜噜噜久久亚洲精品国产品小说| 欧美日韩一区在线播放| 经典三级久久| 亚洲一级免费视频| 免费观看成人鲁鲁鲁鲁鲁视频| 99热这里只有精品8| 久久成人精品一区二区三区| 欧美日韩的一区二区| 精品51国产黑色丝袜高跟鞋| 一区二区激情| 欧美激情1区| 欧美一区二区成人| 欧美日韩免费观看一区二区三区| 狠狠色狠狠色综合人人| 亚洲尤物在线| 亚洲激情成人| 久久精品久久99精品久久| 国产精品av久久久久久麻豆网| 亚洲成人在线视频网站| 性色av一区二区三区红粉影视| 亚洲精品123区| 久久九九免费视频| 国产精品一区二区三区观看| 9人人澡人人爽人人精品| 美女精品国产| 欧美在线黄色| 国产欧美一区二区三区久久人妖| 99精品国产高清一区二区| 裸体一区二区三区| 香港久久久电影| 国产精品羞羞答答| 亚洲午夜精品国产| 亚洲精品乱码久久久久久日本蜜臀| 久久久久久成人| 国产自产2019最新不卡| 欧美一级播放| 亚洲色在线视频| 欧美日韩久久精品| 99re亚洲国产精品| 亚洲国产视频一区| 蜜臀av国产精品久久久久| 精品成人一区二区三区| 久久久午夜精品| 篠田优中文在线播放第一区| 国产伦精品一区二区三区视频孕妇 | 免费成人网www| 久久久av毛片精品| 狠狠色丁香婷婷综合久久片| 久久久久久伊人| 久久成人18免费网站| 国产中文一区二区| 久久久www成人免费精品| 欧美一级精品大片| 国内精品久久久久国产盗摄免费观看完整版| 午夜精品久久久久影视| 亚洲欧美日韩一区二区| 国产精品系列在线播放| 久久成人18免费观看| 欧美在线二区| 在线看片一区| 亚洲风情亚aⅴ在线发布| 欧美精品免费在线| 亚洲性感美女99在线| 一区二区三区视频观看| 国产精品一区二区三区四区| 久久xxxx| 久久久久久夜精品精品免费| 亚洲国产另类久久精品| 亚洲国产欧美日韩精品| 欧美日韩综合一区| 欧美一区二区三区久久精品茉莉花 | 欧美高清免费| 欧美人牲a欧美精品| 亚洲男人影院| 久久本道综合色狠狠五月| 在线不卡欧美| 亚洲三级国产| 国产精品乱看| 美国成人毛片| 欧美激情第10页| 性做久久久久久久免费看| 欧美一区二区三区久久精品茉莉花| 激情亚洲网站| 亚洲精品久久嫩草网站秘色 | 亚洲天堂免费在线观看视频| 国产亚洲精品v| 欧美激情在线播放| 国产精品久久久久99| 美女被久久久| 欧美私人网站| 美女亚洲精品| 欧美视频日韩| 免费成人av在线| 国产精品黄色| 欧美xart系列在线观看| 欧美色精品在线视频| 久久一区二区三区四区| 欧美美女bbbb| 老司机午夜精品| 国产精品第一区| 欧美大片在线影院| 国产精品久久久久久五月尺| 免费一区视频| 国产精品视频福利| 亚洲高清色综合| 国精品一区二区三区| 日韩视频在线观看| 亚洲电影下载| 午夜精品av| 一区二区国产精品| 久久男人av资源网站| 欧美一区二区成人| 欧美成人中文字幕在线| 久久久亚洲高清| 欧美亚洲第一页| 亚洲韩日在线| 在线电影欧美日韩一区二区私密| 亚洲天堂av高清| 日韩亚洲一区二区| 裸体素人女欧美日韩| 性欧美激情精品| 欧美性生交xxxxx久久久| 亚洲福利国产|