(略微修改了格式和錯別字)
今天我在看《
the c++ standard library
》的時候,總覺的上面講的關於
auto_ptr_ref
的問題沒有說清楚,查了網上的資料發現也沒有說清。
也許還有很多朋友像我一樣對此存在疑問。其實,這個問題有沒有弄清楚,對實際程式設計影響並不大,但是本著「勿在浮沙築高台」的精神,我用了乙個晚上的時間,來個徹底的大調查,終於基本上弄明白了其中的道理。(大多數時間都浪費在
vc上,因為
vc支援對右值的
non-const reference
,而標準
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++中,對右值的引用只能是
const
引用。所以說,要在標準
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...