今天我在看the c++ standard library的時候,總覺的上面講的關於auto_ptr_ref的問題沒有說清楚,查了網上的資料發現也沒有說清。 也許還有很多朋友像我一樣對此存在疑問。其實,這個問題有沒有弄清楚,對實際程式設計影像並不大,但是本著「勿在浮沙築高台」的精神,我用了乙個晚上的時間,來個徹底的大調查,終於基本上弄明白了其中的道理。(大多數時間都浪費在vc上,因為 vc支援對右值的非 常應用,而標準c++不可以,所以在vc中,有沒有auto_ptr_ref實際上都無所謂!)
stl中有乙個智慧型指標auto_ptr,可以實現簡單的記憶體自動**,防治記憶體洩漏(memory leakage)。auto_ptr實際是乙個類,在該類析構時自動呼叫delete,從而打到了記憶體**的效果。
但是,由於同乙個指標同一時刻只能被乙個auto_ptr占用,如果採用賦值操作(=)或者拷貝構造函式呼叫等,就會發生所有權轉移,例如:
auto_ptrp(new int(0));
auto_ptrq;
此時,p擁有指向乙個int的指標,q的指標為空。如果執行
q=p;
則,p指向空,q指向int;
但是,這樣所有權轉換的問題同樣發生在引數傳遞中,例如
void foo(auto_ptrt);
如果呼叫 foo(p);
那麼p就丟失了指標,所以乙個解決方法是用引用
例如 void foo1(auto_ptr& t);
void foo2 (const auto_ptr& t);
兩者都是可以的,不過foo1非常不安全,因為在函式裡面很容易通過類似賦值的操作使t丟失指標,而foo2不會。例如
void foo2(const auto_ptr& t)
會發生編譯錯誤,從而避免災難的發生。
但隨之又出現乙個很大的問題,就是auto_ptr類的拷貝建構函式,或者賦值函式。
最理想的情況是這樣(如果能成功,就不會有別的什麼問題):
auto_ptr(const auto_ptr& rhs):ap(rhs.release()){}
但由於上述的原因,會發生編譯錯誤(因為呼叫了release(),而release()會改變成員變數,不再是const)。
所以只能去掉const,變為
auto_ptr(auto_ptr& rhs):ap(rhs.release()){}
這樣可以編譯成功,而且往往也能正確執行,但是唯一的問題是: 當rhs為右值時會出現問題。
為了簡化問題,先假設拷貝建構函式什麼都不做,即:
auto_ptr(auto_ptr& rhs){}
那麼,如果有 auto_ptrp(new int(10))
執行 auto_ptrq(p),不會有任何問題,因為p是左值。
但如果執行 auto_ptrq(auto_ptr(new int(10))) ,則會發生編譯錯誤,因為auto_ptr(new int(10)) 是右值,對右值的引用只能是常引用,也就是"const auto_ptr& rhs"的形式。但這裡要注意的是,剛才那段**用vc編譯沒有任何問題,並且可以順利執行,但是用gcc之類的標準c++就不能順利編譯。
在vc中
auto_ptr& p=auto_ptr(new int(0))是合法的,但在標準c++中是不合法的,只有
const auto_ptr& p=auto_ptr(new int(0))才是合法的,也即在標準c++中,對右值的引用只能是常引用。
所以說,要在標準c++中實現 auto_ptrp(auto_ptr(new int(0))) 就變得不可能了,因為如上所說,拷貝建構函式是這樣的形式:
auto_ptr(auto_ptr& rhs):ap(rhs.release()){}
但是不能把右值傳到乙個非 常引用中。
但畢竟有聰明的人能想到解決辦法,利用**類( proxy class)
宣告如下結構,為了方便,我用int代替模板引數
struct auto_ptr_ref
};然後在auto_ptr類中增加了以下東東:
auto_ptr(auto_ptr_ref rhs):ap(rhs.p){}
auto_ptr& operator=(auto_ptr_ref rhs)
operator auto_ptr_ref()
之後,如果在標準c++有以下呼叫(vc中也會按照這個步驟呼叫,雖然沒有auto_ptr_ref它也能直接呼叫)
auto_ptrp(auto_ptr(new int(0)))
便可以成功,過程如下:
1. 構造臨時物件 auto_ptr(new int(0))
2. 想將臨時物件通過拷貝建構函式傳給p,卻發現沒有合適的拷貝建構函式,因為只有auto_ptr(auto_ptr& rhs)這個不能用,又沒有auto_ptr(const auto_ptr& rhs) (因為用了在所有權轉移中會出錯),呵呵!
3. 編譯器只能曲線救國,看看型別轉換後能不能傳遞。
4. 由於我們定義了 operator auto_ptr_ref() 所以編譯器自然就可以試一下轉為 auto_ptr_ref型別。
5. 編譯器猛然間發現,我們定義了 auto_ptr(auto_ptr_ref rhs):ap(rhs.p){} 的建構函式,可以傳遞。
6. 順利構造p,任務完成。
其實說白了問題很簡單,因為建構函式不能接受右值,則取 中間左值=右值, 然後再讓函式接受中間左值。 而這一系列過程正是利用編譯器能夠自動進行型別轉換而完成的。
文章**:
關於auto ptr ref的一點問題
略微修改了格式和錯別字 今天我在看 the c standard library 的時候,總覺的上面講的關於 auto ptr ref 的問題沒有說清楚,查了網上的資料發現也沒有說清。也許還有很多朋友像我一樣對此存在疑問。其實,這個問題有沒有弄清楚,對實際程式設計影響並不大,但是本著 勿在浮沙築高台...
關於auto ptr ref的一點問題
今天我在看the c standard library的時候,總覺的上面講的關於auto ptr ref的問題沒有說清楚,查了網上的資料發現也沒有說清。也許還有很多朋友像我一樣對此存在疑問。其實,這個問題有沒有弄清楚,對實際程式設計影像並不大,但是本著 勿在浮沙築高台 的精神,我用了乙個晚上的時間,...
關於iBatis selectKey的一點筆記
技術前提 我們使用ibatis作為持久層方案 技術場景 假設我們有兩張表,一張主表main,一張子表sub,並且主表的主鍵是由資料庫維護的自增長的主鍵,子表中有乙個字段引用這個主鍵,那麼當我們插入主表資料後,就需要馬上返回這個自增長的主鍵。解決方案 可以在insert時通過ibatis的select...