[英]How should i return an object from a conversion function if the class of the returned object is defined later?
[英]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.