stl 檔案中的 std::auto_ptr 在c++中的故事特別多, 在它的演變過程中至少出現了3個版本.
http://.josuttis.com/libbook/auto_ptr.html 這個連線裡面有它完整的故事.
vc6中stl帶的auto_ptr( 帶owner欄位)的版本應該就是文中說的version 2.
最新的version裡面包含了乙個auto_ptr_ref, 這個是當將auto_ptr作為函式返回值和函式引數時需要引入的
乙個"額外間接層".
下面是它的一些說明:
auto_ptr的拷貝建構函式和一般我們常見的不同, 它的引數rhs並不是const reference, 而是refence,
auto_ptr( /*const */ auto_ptr& rhs)
假設我們需要將乙個auto_ptr作為某個函式的返回值, 例如
auto_ptrsource()
那麼我們如何在caller中得到返回的結果呢?
理所當然的語法是:
auto_ptrp( source() ); (拷貝建構函式)
或者 auto_ptrp = source(); (拷貝建構函式)
或者auto_ptrp = ...; (operator=)
p = source();
但是如果沒有auto_ptr_ref的存在, 上面這些行實際上應該是乙個編譯錯誤(vc6不報錯), 原因是:
c++中有左值/右值之分, 函式如果返回值, 那麼是r-value. 右值作為reference函式引數時, 只能是const reference.
因此source函式返回的auto_ptr作為rhs實參呼叫auto_ptr的拷貝建構函式時, 只能是const refernce, 但是
這個函式的簽名需要rhs為reference, 因此無法編譯.
舉個最簡單的例子:
有函式:
int foo()
void bar(int & i)
呼叫int& i = foo() ; //錯誤
const int& i = foo(); //ok
bar(foo()) //錯誤
同理, 拷貝建構函式不過是乙個特殊的"函式"而已, 我們上面的source函式返回的auto_ptr物件也只能作為乙個
const auto_ptr&, 但是這個拷貝建構函式需要的引數原型是auto_ptr&, 而不是const auto_ptr& .
因此auto_ptr引入了乙個'額外的間接層' auto_ptr_ref, 來完成乙個從r-value到l-value之間的過渡.
基本的思路是;
提供另外乙個建構函式, 接受乙個以值傳遞的auto_ptr_ref:
auto_ptr( auto_ptr_ref ref)
然後在auto_ptr類中, 提供乙個自動轉型的函式
operator auto_ptr_ref ()
這樣, source返回乙個auto_ptr, 編譯器嘗試呼叫拷貝建構函式, 發現引數不必配(期望const), 然後發現了乙個自動轉型的
operator auto_ptr_ref()函式, 而後又發現通過呼叫該自動轉型得到乙個auto_ptr_ref物件後, 可以呼叫caller的
auto_ptr的以auto_ptr_ref為引數的非explicit的建構函式, 完成了乙個auto_ptr到另外乙個auto_ptr之間的複製過程.
注意一點: operator auto_ptr_ref () 不是const成員函式.
c++語法規則中對於臨時變數的r-value有個詭秘的地方就是:
如果你需要將r-value儲存在乙個reference中, 或者作為某個refence的函式引數, 那麼必須為const reference,
但是你也可以在乙個完整的表示式中直接使用這個臨時變數, 這種情況下該臨時變數實際上並不是作為const reference對待,
因為你可以呼叫它的非const成員函式. 例如:
class integer
private:
int i;
};class integer
private:
int i;
};integer().zero(); //建立乙個integer的臨時變數, 然後呼叫非const成員函式.
這樣, auto_ptrp( source() );
實際上發生的事情就是:
auto_ptrp( source().operator auto_ptr_ref());
通過source()函式得到乙個臨時的auto_ptr物件, 然後呼叫其中的自動轉換函式得到乙個auto_ptr_ref,
即使該轉型函式是非const成員函式仍然可行.
然後呼叫 p 物件的以auto_ptr_ref為引數的建構函式進行複製.
然後, source()函式建立的臨時物件在整個表示式結束後被析構, 當然這個時候這個臨時物件內部的指標已經被reset(0)了,
因為該指標的擁有權已經被p接管了. (否則會重複刪除)
std::auto_ptr在很多情況下是很便利的, 例如乙個函式內部需要通過new分配乙個結構, 那麼誰來釋放是乙個問題,
一種原則是誰分配, 誰釋放, 但是對於這種情況顯然不合適.
利用auto_ptr就簡單得多了: 誰擁有誰釋放, 誰都不要那麼就編譯器自動釋放它. 例如
有個函式:
auto_ptrcreate_sth()
呼叫1:
auto_ptrp = create_sth();
...p退出作用域, 自動釋放.
呼叫2:
create_sth();
沒有人接受這個返回的物件, 那麼編譯器自動會呼叫auto_ptr的析構函式釋放之.
sink也是乙個作用:
例如我已經擁有乙個auto_ptr物件的指標, 那麼我可以定義乙個sink函式, 原型如下:
void sink(auto_ptrp)
正如sink名字暗示的一樣, sink函式起到乙個吸收作用, 將某個外部的auto_ptr物件吸收過來,類似於宇宙中的"黑洞".
例如:auto_ptrp(new sth);
sink(p);
//這裡, p指向null了, p所指的sth物件已經被sink函式"吸收"了.
當然為了防止這種情況在你不注意的情況下發生, 你可以
const auto_ptrp(new sth);
sink(p); //編譯錯誤
auto_ptrp2 = p; //編譯錯誤
這樣, 乙個const auto_ptr的物件一旦構造完成, 永遠不會失去對該物件的擁有權. "一旦擁有, 從不失去".
當然auto_ptr最重要的作用, 也是它的原始目的是為了提供異常安全.
AndroidStudio 動態修改版本名稱
專案經理讓每次打版本的時候去自動改版本名稱,我一想在專案裡面建立個檔案不就行了,每次打包就該一下,然而被打回來了,說 要和svn關聯起來 萬事問度娘,被我找到方法了,但是感覺寫的不是很清楚,那我就分享下第一步引入依賴 注意的是在下面的gradle裡面去引入 classpath group org.t...
openssl修改版本號
1.檢視當前openssl版本號 openssl version2.檢視openssl所在位置 which openssl3.檢視儲存版本號的libcrypto.so所在位置 ldd usr bin openssl grep libcrypto.so4.備份找到的libcrypto.so cp us...
ubuntu gitlab修改版本倉庫路徑
gitlab的倉庫的路徑預設是在 var opt gitlab git data repositories,為了系統盤在損壞或者異常的情況下,倉庫不受影響。修改方法 建立gitlab倉庫 mkdir share gitlab mkdir share gitlab git data chmod 777...