簡體   English   中英

最好返回C ++引用或weak_ptr?

[英]Better to return a C++ reference or a weak_ptr?

假設我有一個類,我希望用戶能夠引用我的一個成員。 哪個更受歡迎?

class Member;

class ClassWithWeakPtr
{
private:
   boost::shared_ptr<Member> _member;
public:
   boost::weak_ptr<Member> GetMember();
};

要么

class Member;

class ClassWithCppReference
{
private:
    Member _member;
public:
    Member& GetMember() {return _member;}
};

你怎么看? 什么時候比另一個更好?

為什么不返回shared_ptr<> 那么客戶端可以根據需要使用返回的內容,但是如果“服務器”類消失則沒有問題。

沒有太多情況下weak_ptr<>的語義很有意義(緩存和???)。 通常當客戶要求某事時,它希望擁有由客戶自己確定的那個東西(並且共享所有權和完全所有權一樣好)。

如果你有一個情況,可以在不知道客戶端的情況下銷毀'server'對象(你可能想要use weak_ptr<>shared_ptr<> ),你可以做的最糟糕的事情是返回對成員的引用。 在這種情況下,客戶端無法知道訪問返回的引用是否安全。 您必須返回該成員的副本或智能指針,該指針可以正確地管理要返回的成員的生命周期。

請記住,如果有一個表達式產生一個臨時的ClassWithCppReference (並不總是很明顯),那么調用GetMember()的客戶端甚至不能在下一個語句中使用返回的引用。

我想提出一些內容來回應關於效率等的評論(主要來自OP和Colomon)。在真實性能方面,復制內容往往無關緊要。

我編寫的程序可以進行大量的防御性復制 這是Java中的一個習慣用法,因為所有對象都是通過指針傳遞的,所以會有很多別名,所以你可以復制進出類的任何內容來打破別名,確保你的類的客戶端不會違反你的類不變量通過在事后修改對象。

我寫過的程序在各個地方都有防御性地復制整個結構,包括整個列表和地圖。 為了確保性能不受影響,我對該程序進行了廣泛的分析。 主要的瓶頸在其他地方(即便如此,我調整了其他地方,以便留下的主要瓶頸是網絡)。 這個防御性的復制形象沒有進入該計划的熱點。


ETA:我覺得有必要澄清我的信息,因為一位評論者的閱讀方式與我的預期不同,很可能其他人也這樣做了。 我的觀點並不是可以在willy-nilly周圍復制東西; 但是,應該總是分析他們的代碼的性能,而不是瘋狂地猜測它將如何執行。

有時,當復制整個結構仍然提供可接受的性能,同時使代碼更容易編寫,那么在我看來,這是一個更好的權衡(在大多數情況下)比代碼只是稍微快一些但更復雜。

你應該避免放棄你的內部; 這是准則n。 42個“C ++編碼標准”(Herb Sutter和Andrei Alexandrescu)。 如果由於某種原因,你有,更好地返回一個const引用 ,而不是一個指針,因為常量性不通過它傳播。

weak_ptr <>似乎是一個可行的解決方案,即使它的基本目的是避免shared_ptr的循環。 相反,如果你返回一個shared_ptr <>,你會延長這種內部的生命,這在大多數情況下是沒有意義的。

當有人處理對其內部的引用時,實例消失的問題應該面對線程之間的正確同步/通信。

我認為唯一合理的答案是,它取決於成員與班級的關系,以及您希望班級用戶能夠做什么。 _member是否具有獨立於Class對象的有意義的存在? 如果沒有,那么我認為使用shared_ptr沒有任何意義,無論你是返回weak_ptr還是shared_ptr 基本上要么是讓用戶訪問一個可能比賦予它意義的Class對象更長的Member對象。 這可能會防止崩潰,但代價是隱藏嚴重的設計錯誤。

正如awgn所說,你應該非常小心暴露你的班級內部。 但我認為肯定有一席之地。 例如,我的代碼中有一個表示文件對象的類,由文件頭對象和文件數據對象組成。 完全復制文件類中的頭部接口將是愚蠢的並且違反DRY。 我想,您可以強制用戶獲取頭對象的副本,對副本進行更改,然后將外部對象復制回整個文件類。 但是這引入了很多低效率,只能讓你能夠使頭文件對象表示不同於頭對象表示。 如果您確定這不是您想要做的事情,那么您也可以返回對標題的非const引用 - 它更簡單,更有效。

返回一個弱指針肯定會更昂貴並且沒有任何實際意義 - 你無論如何都不能堅持所有權的時間長於主要對象的生命周期。

為什么返回弱指針? 我認為你讓它變得更復雜,沒有任何必要的好處。

在大多數情況下,我會提供

const Member& GetMember() const;
Member& GetMember(); // ideally, only if clients can modify it without
                     // breaking any invariants of the Class

weak_ptr< Member > GetMemberWptr(); // only if there is a specific need
                                    // for holding a weak pointer.

這背后的基本原理是(我也可能是其他許多人)調用方法GetMember()意味着Class擁有member ,因此返回的引用只對包含對象的生命周期有效。

在我遇到的大多數(但不是全部)代碼中, GetMember()方法通常用於立即對返回的成員執行操作,而不是為以后的用戶存儲它。 畢竟,如果您需要訪問該成員,您始終可以在需要時隨時從包含對象請求它。

我基本的,無知的指導方針:

我意識到后者可能是不安全的,如果ClassWithCppReference的實例可能會消失,而有人仍然有一個成員的引用。 但是,我也可以看到后一個類的POD類型的參數,例如,對設備的消息,並且您不想一直復制該成員。

根據具體情況,任何一方都可以。 將“實時”鏈接返回給成員的主要問題(如果您必須首先公開一個)是使用公開成員的任何人是您的客戶端可能持有的時間比包含對象的存在時間長。 如果您的客戶端在其包含對象超出范圍時通過引用訪問該成員,您將面臨“奇怪”的崩潰,錯誤的值和類似的樂趣。 不建議。

返回weak_ptr <>的主要優點是它能夠與客戶端通信他們正在嘗試訪問的對象已經消失,而引用無法做到。

我的2便士的價值是:

  • 如果您的客戶都不會使用該成員,那么您作為作者是唯一使用它並控制對象生命周期的人,返回引用就可以了。 Const引用會更好,但現有代碼在現實世界中並不總是可行。
  • 如果有其他人訪問並使用該成員,特別是如果您正在編寫庫,則返回weak_ptr <>。 它將為您節省很多麻煩,並使調試更容易。
  • 我不會返回shared_ptr <>。 我已經看過這種方法,並且它通常受到對weak_ptr / weak引用概念感到不舒服的人的青睞。 我看到它的主要缺點是它會人為地延長另一個對象成員的生命周期,超出其包含對象的范圍。 這在99%的情況下在概念上是錯誤的,更糟糕​​的是,可以將您的編程錯誤(訪問不再存在的東西)變成概念錯誤(訪問應該存在的東西)。

暫無
暫無

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

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