如簡介所述,如果物件存在於侵入式容器中 使用者必須明確地為物件留下容器簿記 機器學習是向機器提供資料和答案 讓機器自行探索規則的科學本節會詳細說明 系統內允許您存放物件的所有容器該指令會執行以下動作:
- 示範可能存在於單一物件中的物件單純 都會在 Docker 容器中執行
- 顯示兩種允許同時加入多個容器的方式。
- 示範如何完全手動控管 Google 文件儲存空間 物件。
單一容器成員資格,並混合使用。
一般來說,最簡單的方式就是選擇使用容器的預設混合 選擇適當的選項您已經看過這個連結頁面看起來會是什麼樣子 清單,這裡 都是所有預設組合的簡易範例。
class FooObj : public fbl::SinglyLinkedListable<FooObj*> { /* ... */ };
using StackOfFoos = fbl::SinglyLinkedList<FooObj*>;
class FooObj : public fbl::DoublyLinkedListable<FooObj*> { /* ... */ };
using QueueOfFoos = fbl::DoublyLinkedList<FooObj*>;
class FooObj : public fbl::WAVLTreeContainable<FooObj*> { /* ... */ };
using MapOfIntToFoos = fbl::WAVLTree<int, FooObj*>;
// Hash tables default to singly linked list buckets
class FooObj : public fbl::SinglyLinkedListable<FooObj*> { /* ... */ };
using SLLHashOfIntToFoos = fbl::HashTable<int, FooObj*>;
// But you can use a doubly linked list as well.
class FooObj : public fbl::DoublyLinkedListable<FooObj*> { /* ... */ };
using DLLHashOfIntToFoos = fbl::HashTable<int, FooObj*,
fbl::DoublyLinkedList<FooObj*>>;
這些範例都會使用指向 FooObj
的原始指標。如果您負責管理
透過 std::unique_ptr
或 fbl::RefPtr
語意建構物件
視需要替換,前提是比對中混合的指標類型
容器的指標類型
每個物件可以存在於容器的單一「類型」中,但這樣做 這些執行個體不會繫結到容器的單一執行個體。例如:
class Message : public fbl::DoublyLinkedListable<std::unique_ptr<Message>> { /* ... */ };
class TransmitQueue {
public:
// ...
void SendMessage(Payload payload) {
// Get a free message to send, or allocate if there are no free messages.
std::unique_ptr<Message> tx;
if (fbl::AutoLock lock(&lock_); free_messages_.is_empty()) {
tx = std::make_unique<Message>();
} else {
tx = free_messages_.pop_front();
}
tx.PrepareMessage(std::move(payload));
{
fbl::AutoLock lock(&lock_);
tx_pending_messages_.push_back(std::move(tx));
}
SignalTxThread();
}
// ...
private:
fbl::Mutex lock_;
fbl::DoublyLinkedList<std::unique_ptr<Message>> free_messages_ TA_GUARDED(lock_);
fbl::DoublyLinkedList<std::unique_ptr<Message>> tx_pending_messages_ TA_GUARDED(lock_);
};
這個範例中的 Message
物件可以存在於
DoublyLinkedList
不重複的指標,指向 Messages
、,但只有
在此範例中,系統會保留免費訊息清單。
發送訊息的時機:
- 系統會從免費清單中移除訊息, 免費清單是空的。
- 已備妥訊息的酬載。
- 系統會將訊息排入待處理佇列。
- 最後,背景工作執行緒會遭到鎖定,以告知其有訊息正在等待訊息 等候處理佇列。
worker 完成後,就會將 Message
物件移回免費的
可能會再次使用這個程式碼,但並未在這裡顯示。
以多種混合形式使用多個容器成員資格
如果物件必須同時存在於多個容器中,該怎麼辦?如果 和容器類型本身不同 多重預設混合只要將混合使用新增至基本類別清單即可 屬性。例如:
class FooObj : public fbl::RefCounted<FooObj>,
public fbl::DoublyLinkedListable<fbl::RefPtr<FooObj>>,
public fbl::WAVLTreeContainable<fbl::RefPtr<FooObj>> { /* ... */ };
using UniqueId = uint64_t;
static fbl::WAVLTree<UniqueId, fbl::RefPtr<FooObj>> g_active_foos;
static fbl::DoublyLinkedList<fbl::RefPtr<FooObj>> g_process_pending;
zx_status_t ProcessFooWithId(UniqueId id) {
if (auto iter = g_active_foos.find(unique_id); iter.IsValid()) {
if ((*iter).DoublyLinkedListable<fbl::RefPtr<FooObj>>::InContainer()) {
return ZX_ERR_BAD_STATE;
}
g_process_pending.push_back(iter.CopyPointer());
PokeWorkerThread();
} else {
return ZX_ERR_NOT_FOUND;
}
return ZX_OK;
}
在此範例中,系統中所有有效 FooObj
的樹狀結構
例如:樹狀結構中的物件會由其 UniqueId
(也就是
通常是大整數)。目前也有 FooObj
的佇列
以便處理。ProcessFooWithId
函式會嘗試使用
指定的 ID 並在 g_process_pending
佇列中加入該 ID 的參照。
請注意,如果在有效的物件集中發現物件,系統會檢查該物件
嘗試附加前,請確認該佇列並未已經在待處理佇列中
並排入待處理佇列FooObj
可以同時存在於待處理佇列和
運作中集合,但不能重複存在於待處理佇列中。
如果發生以下情況,則嘗試將物件放入 ContainerTypeA
的例項
物件在 ContainerTypeA
的例項中「已經」是同一個例項
如果啟用斷言,就會觸發 ZX_DEBUG_ASSERT
否則就會毀損程式狀態。非常重要
確保不會發生這類情況程式中的經常性變化可以確保
雖然不可能發生,但如果您的程式缺少這樣的不變體,請記得
檢查物件是否位於容器中
請參閱測試容器成員資格一節。
介紹各種不同的建構方式另外也請注意,加入會員測試的難易度為何
也就是用的是例子您稍後將看到更好的做法。
子節,說明 ContainableBaseClasses
的使用。
關於這個例子,還有最後一點要說明。所以說
FooObj
新增至待處理佇列,物件的新 fbl::RefPtr
執行個體
執行個體需要提供給 push_back
。您可以呼叫
疊代器的 CopyPointer
方法,這個方法會叫用複製建構函式
,為物件提供新的參照。
對原始指標而言,此為免人工管理。如果是 unique_ptrs,此為非法行為
因而無法編譯
使用 ContainableBaseClasses 的多個容器成員資格
如果物件分散在多個容器中,該怎麼做
相同的基本類型呢?最簡單的方法就是
搭配使用 fbl::ContainableBaseClasses
與類型標記,可呈現
,用於識別物件可能存在於的不同容器以下是
重新導入上一個範例,但這次增加
物件可能存在於另一個清單
struct ActiveTag {};
struct ProcessPendingTag {};
struct OtherListTag {};
class FooObj :
public fbl::RefCounted<FooObj>,
public fbl::ContainableBaseClasses<
fbl::TaggedDoublyLinkedListable<fbl::RefPtr<FooObj>, ProcessPendingTag>,
fbl::TaggedDoublyLinkedListable<fbl::RefPtr<FooObj>, OtherListTag>,
fbl::TaggedWAVLTreeContainable<fbl::RefPtr<FooObj>, ActiveTag>> { /* ... */ };
using UniqueId = uint64_t;
static fbl::TaggedWAVLTree<UniqueId, fbl::RefPtr<FooObj>, ActiveTag> g_active_foos;
static fbl::TaggedDoublyLinkedList<fbl::RefPtr<FooObj>, OtherListTag> g_process_pending_foos;
zx_status_t ProcessFooWithId(UniqueId id) {
if (auto iter = g_active_foos.find(unique_id); iter.IsValid()) {
if (fbl::InContainer<ProcessPendingTag>(*iter)) {
return ZX_ERR_BAD_STATE;
}
iter->SetPriority(fbl::InContainer<OtherTag>(*iter) ? 20 : 10);
g_process_pending_foos.push_back(iter.CopyPointer());
} else {
return ZX_ERR_NOT_FOUND;
}
}
這個範例先定義 3 種 (「標記」) 用來
找出要與 FooObj
並行使用的不同容器。這些
類型並不實際上「不會」執行任何動作,而是空白結構。您將
切勿將任何物件執行個體化只有一種不重複的類型
編譯器能用來瞭解與哪個清單類型配對
節點狀態在這個範例中,
具有 ProcessPendingTag
的 TaggedDoublyLinkedListable
是使用的節點狀態
由 g_process_pending_foos
清單排序
請注意,這也有助於讓 InContainer
測試更容易讀取。使用標記
我們就能叫用獨立的 fbl::InContainer<>
函式,並傳遞
對物件使用 const&
,並指定物件應使用的容器類型
將經過測試,檢查是否使用標記
ContainableBaseClasses
可與任何可納入的混合項目和
可讓物件存在於任意數量的容器類型中
每個容器類型都有自己獨特的類型
避免混合使用 ContainableBaseClasses 和預設混合
雖然「技術上」ContainableBaseClasses
可與
這並不算是最佳做法,因此應避免使用。
開始使用標記時,肯定需要額外輸入一些資訊
ContainableBaseClases
:管理物件的容器成員資格 (建立後)
開始簡化模式一致性:
一律使用含有指定物件的標記 (而非有時候使用標記
不會) 影響可讀性和可維護性,特別是在
測試容器成員資格
容器類型定義會使用物件中的節點儲存空間
因此,請勿這麼做:
namespace FooTags {
struct SortByBase {};
struct SortBySize {};
}
class Foo :
public DoublyLinkedListable<Foo*>, // For the pending processing queue
public fbl::ContainableBaseClasses<
public TaggedWAVLTreeContainable<Foo*, FooTags::SortByBase>,
public TaggedWAVLTreeContainable<Foo*, FooTags::SortBySize>> { /* ... */ };
而是改用類似下方的內容:
namespace FooTags {
struct PendingProcessing {};
struct SortByBase {};
struct SortBySize {};
}
class Foo :
public fbl::ContainableBaseClasses<
public TaggedDoublyLinkedListable<Foo*, FooTags::PendingProcessing>,
public TaggedWAVLTreeContainable<Foo*, FooTags::SortByBase>,
public TaggedWAVLTreeContainable<Foo*, FooTags::SortBySize>> { /* ... */ };
使用明確節點和自訂特徵的容器成員資格
最後,還有一個選項可控管物件的容器成員資格。 這是最低程度的選項,對於撰寫、理解 建構應用程式只有在具體技術要求的情況下,才能使用 必須強制您遵守這些規定以下列舉幾個可能原因 說明明確節點和自訂特徵的使用合理性 物件容器成員資格
- 物件必須具備 C++ 標準版面配置,因此無法繼承 您的任何組合
- 您必須精確控制物件節點儲存空間中的「位置」 且不能最終放在編譯器分配的儲存空間 基礎類別。
- 您的物件屬於複雜類別階層,當中有不同的 每個階層都必須可在不同的容器中保存使用 不同程度的混合輔助程式會導致模稜兩可和 也因為繼承關係
每個基本容器類型都有相關聯的 NodeState
類型。非
出乎意料的是,他們的名稱是:
SinglyLinkedListNodeState<PtrType>
DoublyLinkedListNodeState<PtrType>
WAVLTreeNodeState<PtrType>
這些結構的用途是保留 容器的資料結構使用方法需要:
以下範例中的物件可能存在於兩份連結的清單中 明確的節點和自訂特徵:
class Obj {
public:
// Obj impl here
private:
struct FooListTraits {
static auto& node_state(Obj& obj) {
return obj.foo_list_node_;
}
};
struct BarListTraits {
static auto& node_state(Obj& obj) {
return obj.bar_list_node_;
}
};
friend struct FooListTraits;
friend struct BarListTraits;
fbl::DoublyLinkedListNodeState<Obj*> foo_list_node_;
fbl::DoublyLinkedListNodeState<fbl::RefPtr<Obj>> bar_list_node_;
public:
using FooList = fbl::DoublyLinkedListCustomTraits<Obj*, FooListTraits>;
using BarList = fbl::DoublyLinkedListCustomTraits<fbl::RefPtr<Obj>, BarListTraits>;
};
新增節點狀態簿記
這些行宣告了 Obj
所需的儲存空間在兩種不同的
可能會同時連結多個清單
fbl::DoublyLinkedListNodeState<Obj*> foo_list_node_;
fbl::DoublyLinkedListNodeState<fbl::RefPtr<Obj>> bar_list_node_;
必須為節點和需求指定用於追蹤的指標類型
與容器對應的版本在此範例中,foo_list_node_
是節點
狀態物件可供清單使用,以追蹤使用原始
指標,bar_list_node_
則是節點狀態物件
用於追蹤使用 fbl::RefPtr<>
物件的清單。最佳做法是
將這些節點狀態物件設為類別的私人成員。
定義節點狀態特徵類別
這行程式碼宣告兩個「特徵」用來向容器說明 瞭解如何存取相關聯的節點簿記功能
struct FooListTraits {
static auto& node_state(Obj& obj) {
return obj.foo_list_node_;
}
};
struct BarListTraits {
static auto& node_state(Obj& obj) {
return obj.bar_list_node_;
}
};
這些類別不含成員變數或方法,只是單一靜態方法
名為 node_state
,後者會採用物件類型的可變動參照
會傳回適當的節點狀態簿記例項的可變動參照
物件。這些類別不會執行個體化,只會用於
在編譯期間,容器與
每個物件專屬的簿記儲存空間。
請注意,為遵循最佳做法,節點狀態執行個體為不公開
Obj
位成員。最佳做法是將特徵類別設為不公開
但由於節點執行個體的私人性質,您也需要
將特徵類別宣告為物件的好友。在這個例子中
就會處理這項工作
friend struct FooListTraits;
friend struct BarListTraits;
定義容器類型,以及指定容器要使用的節點狀態儲存空間
最後,您需要定義容器類型才能搭配 物件,並將這些類型提供給物件使用者。在本 例如負責該工作的幾行。
public:
using FooList = fbl::DoublyLinkedListCustomTraits<Obj*, FooListTraits>;
using BarList = fbl::DoublyLinkedListCustomTraits<fbl::RefPtr<Obj>, BarListTraits>;
請注意,我們使用其中一個 using
專用別名
DoublyLinkedList
,特別是 DoublyLinkedListCustomTraits
。這個別名
重新排列範本參數的排列順序
傳遞至清單類型的參數定義了
即可找到合適的節點狀態簿記儲存空間
特徵類別和節點狀態儲存空間都是 Obj
的私人成員。
因此請務必為容器類型訂定公開定義
物件使用者可取得這個權限將讓他們說出:
以下內容。
Obj obj_instance;
Obj::FooList list;
list.push_back(&obj_instance);