簡體   English   中英

我該如何從函數中返回一個對象?

[英]How should I return an object from a function?

請考慮以下情形:有一個類CDriver負責枚舉所有連接的輸出設備(由COutput類表示)。 代碼可能如下所示:

class COutput
{
    // COutput stuff
};

class CDriver
{
public:
    CDriver();  // enumerate outputs and store in m_outputs

    // some other methods

private:
    std::vector<COutput> m_outputs;
};

現在, CDriver應該能夠授予用戶訪問枚舉的COutput的權限。

實現此目的的第一種方法是返回一個指針:

const COutput* GetOutput(unsigned int idx) const 
{ 
    return idx < m_outputs.size() ? &m_outputs[idx] : nullptr; 
}

我看到它的方式,這個方法提出的問題是,如果指針由用戶存儲並且在CDriver對象被銷毀后它仍然存在,那么它現在是一個懸空指針。 這是由於這樣的事實,而指向( COutput對象)具有的析構函數中被摧毀CDriver對象。

第二種方式是通過引用返回:

const COutput& GetOutput(unsigned int idx) const 
{ 
    return idx < m_outputs.size() ? &m_outputs[idx] : m_invalidOutput; 
}

這里同樣的問題適用於帶指針的方法。 此外,還有一個警告,即不能返回真正的無效對象。 如果將nullptr作為返回指針返回,則很明顯它是“無效的”。 但是,當涉及引用時,沒有相當於nullptr

繼續接近第三。 按價值返回。

COutput GetOutput(unsigned int idx) const 
{ 
    return idx < m_outputs.size() ? &m_outputs[idx] : m_invalidOutput; 
}

在這里,用戶不必擔心返回對象的生命周期。 但是,必須復制COutput對象,並且與參考方法COutput ,沒有直觀的方法來檢查錯誤。

我可以繼續......

例如, COutput對象可以在堆上分配並存儲在std::shared_ptr並返回。 然而,這會使代碼非常冗長。

有沒有辦法直觀地解決這個問題,而不會引入不必要的代碼冗長?

讓我先說,你絕對不應該開始亂用shared_ptr來解決這個問題。 只是不要這樣做。 這里有一些合理的選擇。

首先,您可以簡單地按價值返回。 如果COutput很小,這是一個很好的方法。 要處理越界索引,您有兩個選擇。 一個是拋出異常。 效果很好,很容易。 這是我最有可能推薦的。 確保有一個size()成員,用戶可以調用它來獲得大小,這樣他們可以避免支付投擲費用,如果這對他們來說太貴了。 您還可以返回一個optional 這是在17之前的標准庫中,在boost之前,並且有獨立的實現。

其次,您可以通過指針/引用返回。 是的,它可以搖擺不定。 但C ++並沒有聲稱可以提供針對此的保護。 每個標准容器的特性begin()end()方法返回迭代器也很容易懸掛。 期待客戶避免這些陷阱在C ++中並非不合理(你當然應該記錄它們)。

第三,您可以進行控制反轉:而不是讓用戶操作對象,而是讓用戶傳遞他們想要采取的操作。 換一種說法:

template <class F>
auto apply(std::size_t idx, F f) const 
{
    if (idx >= m_outputs.size())
    {
        throw std::out_of_range("index is out of range");
    }

    return f(m_outputs[idx]); 
}

用法:

CDriver x;
x.apply(3, [] (const COutput& o) {
    o.do_something();
});

在這種情況下,用戶需要更加努力地工作(盡管它仍然可能),因為它們沒有傳遞指針/引用,並且您也不必復制。

你當然可以通過多種方式改變apply ; 例如,不從函數調用返回,而是返回true / false以指示索引是否在范圍內而不是拋出。 基本思路是一樣的。 請注意,必須修改此方法以與虛函數結合使用,這將使其不太理想。 因此,如果您正在考慮CDriver的多態性,您應該考慮這一點。

看看C ++ 11的共享指針。 使用共享指針,在所有共享指針“擁有”該對象被銷毀之前,不會調用基礎對象的解構函數。 在處理對單個對象的多個引用時,這會消除很多(但不是全部)頭痛的問題。

這里有更多信息: http//en.cppreference.com/w/cpp/memory/shared_ptr

1)。 經過試驗和測試:拋出標准參數異常

2)你可以使用元組和std :: tie

const std::tuple<bool, COutput> GetOutput(unsigned int idx) const 
{ 
     return idx < m_outputs.size() 
               ? std::make_tuple(true m_outputs[idx])
               : std::make_tuple(false,  m_invalidOutput); 
}

bool has_value;
COutput output;

std::tie(has_value, output) = GetOutput(3);

要在C ++ 17中替換元組和std :: tie,可以使用結構化綁定

3)對於這種情況,C ++ 17將具有std :: optional

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM