關於auto_ptr_ref的一點問題

來源:互聯網
上載者:User

今天我在看 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_ptr<int> p(new int(0));

auto_ptr<int> q;

此時,p擁有指向一個int的指標,q的指標為空白。如果執行

q=p;

則,p指向空,q指向int;

但是,這樣所有權轉換的問題同樣發生在參數傳遞中,例如

void foo(auto_ptr<int> t);

如果調用 foo(p);

那麼p就丟失了指標,所以一個解決方案是用引用

例如 void foo1(auto_ptr<int>& t);

         void foo2 (const auto_ptr<int>& t);

兩者都是可以的,不過foo1非常不安全,因為在函數裡面很容易通過類似賦值的操作使t丟失指標,而foo2不會。例如

void foo2(const auto_ptr<int>& t)

{ auto_ptr<int> m; m=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_ptr<int> p(new int(10))

執行 auto_ptr<int> q(p),不會有任何問題,因為p是左值。

但如果執行      auto_ptr<int> q(auto_ptr<int>(new int(10))) ,則會發生編譯錯誤,因為auto_ptr<int>(new int(10)) 是右值,對右值的引用只能是常引用,也就是"const auto_ptr& rhs"的形式。但這裡要注意的是,剛才那段代碼用VC編譯沒有任何問題,並且可以順利運行,但是用GCC之類的標準c++就不能順利編譯。

在VC中

auto_ptr<int>& p=auto_ptr<int>(new int(0))     是合法的,但在標準C++中是不合法的,只有

const auto_ptr<int>& p=auto_ptr<int>(new int(0)) 才是合法的,也即在標準C++中,對右值的引用只能是常引用。

所以說,要在標準C++中實現 auto_ptr<int> p(auto_ptr<int>(new int(0))) 就變得不可能了,因為如上所說,拷貝建構函式是這樣的形式:

auto_ptr(auto_ptr<T>& rhs):ap(rhs.release()){}

但是不能把右值傳到一個非 常引用中。

但畢竟有聰明的人能想到解決辦法,利用代理類( proxy class)

聲明如下結構,為了方便,我用int代替模板參數

struct auto_ptr_ref

{

   int* p;

   auto_ptr_ref(int *t):p(t){}

};

然後在auto_ptr類中增加了以下東東:

auto_ptr(auto_ptr_ref rhs):ap(rhs.p){}

auto_ptr& operator=(auto_ptr_ref rhs){reset(rhs.p); return *this;}

operator auto_ptr_ref(){return auto_ptr_ref(release());}

之後,如果在標準C++有以下調用(VC中也會按照這個步驟調用,雖然沒有auto_ptr_ref它也能直接調用)

auto_ptr<int> p(auto_ptr<int>(new int(0)))

便可以成功,過程如下:

1. 構造臨時對象 auto_ptr<int>(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,任務完成。

其實說白了問題很簡單,因為建構函式不能接受右值,則取 中間左值=右值, 然後再讓函數接受中間左值。 而這一系列過程正是利用編譯器能夠自動進行類型轉換而完成的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.