2015-04-03 61 views
2

根據cppreference調用std::unique_ptr::operator*()相當於調用*(std::unique_ptr::get())unique_ptr自定義指針類型:* get()和operator *()給出不同的輸出

但是我得到不同的結果這兩個調用。這裏是我的代碼:

#include <iostream> 
#include <string> 
#include <memory> 

#include <fcntl.h> 
#include <unistd.h> 

struct file_descriptor 
{ 
private: 
    struct closer; 

public: 
    typedef int handle_type; 
    typedef closer closer_type; 

    constexpr static handle_type kInvalidHandle = -1; 

public: 
    file_descriptor(int handle = kInvalidHandle) : handle_{ handle } { } 
    file_descriptor(std::nullptr_t) : file_descriptor{ } { } 

    operator int&() { return handle_; } 
    operator int() const { return handle_; } 

    int& operator*() { return static_cast<int&>(*this); } 
    int operator*() const { return static_cast<int>(*this); } 

    bool operator==(const file_descriptor& other) const 
    { return (handle_ == other.handle_); } 

    bool operator!=(const file_descriptor& other) const 
    { return !(*this == other); } 

private: 
    struct closer 
    { 
    typedef file_descriptor pointer; 

    void operator()(pointer handle) const 
    { ::close(*handle); } 
    }; 

    int handle_; 
}; 

using unique_file_ptr = std::unique_ptr<typename file_descriptor::handle_type, 
             typename file_descriptor::closer_type>; 

unique_file_ptr managed_open(const std::string& path) 
{ 
    return { ::open(path.c_str(), O_RDWR), { } }; 
} 

int main(int, char**) 
{ 
    auto handle = managed_open("/dev/random"); 
    std::cout << "*handle  : " << *handle << std::endl; 
    std::cout << "*handle.get(): " << *handle.get() << std::endl; 
} 

我的輸出(實時輸出here):

*handle  : 4198400 
*handle.get(): 3 

請注意*handle.get()返回正確的值,而*handle沒有。

爲什麼我會得到不同的結果?

+0

@Lingxi否:'decltype(&:: open)= int(*)(const char *,int,...)'。我想這是因爲'file_descriptor'可以從'int'構造。 – 2015-04-03 15:54:08

+0

有趣的事情發生,因爲你也重載'file_descriptor :: operator *'?嘗試刪除,看看你得到什麼。 – 2015-04-03 16:02:29

+1

@RichardHodges有趣的是,當我註釋掉'operator *()'時,出現以下錯誤:'.../include/C++/4.8/bits/unique_ptr.h:222:9:error:indirection requires指針操作數('pointer'(aka'file_descriptor')無效) return * get();',這意味着'unique_ptr :: operator *()'等同於'* get()'。這使我相信我在代碼中以某種方式調用UB。 – 2015-04-03 16:08:11

回答

6

這是發生了什麼。 unique_ptr<T, D>::get()返回D::pointer - 在你的情況下,從int句柄構造的臨時file_descriptor。然後它的operator*調用它的operator int&,它返回對該臨時內存儲的handle_的引用。

當您直接致電*handle.get()時,它產生的int&引用在臨時死亡之前使用。

但是,如果您撥打*handle,您可以撥打handle.operator*(),然後撥打handle.get()。的operator*的執行情況,一切都擴大了,變成這樣的事情:

int& operator*() { 
    file_descriptor temp(internal_pointer_); 
    int& result = temp.handle_; 
    return result; 
} 

該函數返回一個臨時的成員的引用。臨時支架處臨時死亡,並且參考變得懸空,於是程序展示在其生命週期結束後訪問對象時觸發的未定義行爲。

+1

因此'unique_ptr :: pointer'應該最好是一個真正的指針類型:p – Lingxi 2015-04-03 16:43:42

+0

當我在stdlib中查看unique_ptr的代碼時,我得出了同樣的結論。雖然有些人可能會爭論我的代碼的可用性,但我只是玩弄unique_ptr,因爲它似乎是獲得一些RAI行爲的簡單方法。我只是想嘗試找到一種方法,使其與unique_ptr(用於_educational_目的)工作。 – 2015-04-03 16:53:36

+1

@靈溪不一定。它可以成爲代理類 - 但代理對象不應該複製被代理的對象。話雖如此,在這個特殊的例子中'file_descriptor'似乎沒有任何有用的用途。 – 2015-04-03 16:55:35

0

我建議你重新考慮解決方案。這裏有一個可靠的方法來處理它:

#include <iostream> 
#include <string> 
#include <memory> 
#include <stdexcept> 
#include <utility> 

#include <fcntl.h> 
#include <unistd.h> 

struct managed_file { 
    managed_file() noexcept {}; 
    managed_file(const std::string& path) 
    : _handle { ::open(path.c_str(), O_RDWR) } 
    { 
     if (_handle == -1) { 
      throw std::runtime_error { std::string { "failed to open file " } + path }; 
     } 
    } 

    managed_file(const managed_file&) = delete; 
    managed_file(managed_file&& other) noexcept 
    : _handle { other._handle } 
    { 
     other._handle = -1; 
    } 

    managed_file& operator=(const managed_file&) = delete; 
    managed_file& operator=(managed_file&& other) noexcept { 
     managed_file tmp { std::move(other) }; 
     using std::swap; 
     swap(_handle, other._handle); 
     return *this; 
    } 

    virtual ~managed_file() noexcept 
    { 
     close(); 
    } 

    void close() noexcept { 
     if (_handle != -1) { 
      ::close(_handle); 
      _handle = -1; 
     } 
    } 

    const int& operator*() const noexcept { 
     return _handle; 
    } 

private: 
    int _handle = -1; 
}; 

managed_file managed_open(const std::string& path) 
{ 
    return managed_file { path }; 
} 

using namespace std; 

int main(int, char**) 
{ 

    cout << "opening" << endl; 
    auto handle = managed_open("/dev/random"); 

    cout << "checking" << endl; 
    std::cout << "*handle  : " << *handle << std::endl; 
} 
+0

我想操作系統試圖重用'std :: unique_ptr'。 – Lingxi 2015-04-03 16:54:06

+0

當然我明白 - 但這並不是他嘗試這樣做的方式。 – 2015-04-03 17:11:57